7#include <private/bitstreams_p.h>
9#include <QtCore/private/qnumeric_p.h>
10#include <QtCore/private/qiodevice_p.h>
11#include <QtCore/private/qnoncontiguousbytedevice_p.h>
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/QRandomGenerator>
14#include <QtCore/qloggingcategory.h>
23using namespace Qt::StringLiterals;
27
28
29
30
31
32
33
34
35
37QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID)
noexcept
38 : QObject(connection), m_streamID(streamID)
42 qCDebug(qHttp2ConnectionLog,
"[%p] new stream %u", connection, streamID);
45QHttp2Stream::~QHttp2Stream()
noexcept {
46 if (
auto *connection = getConnection()) {
47 if (m_state != State::Idle && m_state != State::Closed) {
48 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, destroyed while still open", connection,
51 if (connection->getSocket()) {
52 if (isUploadingDATA())
53 sendRST_STREAM(CANCEL);
55 sendRST_STREAM(HTTP2_NO_ERROR);
59 connection->m_streams.remove(streamID());
64
65
66
67
70
71
72
73
74
75
76
77
78
79
80
81
82
85
86
87
88
89
90
91
94
95
96
97
98
99
100
101
104
105
106
107
108
109
110
111
112
113
114
118
119
120
121
122
123
124
127
128
129
130
131
132
133
134
137
138
139
140
141
142
143
144
147
148
149
150
151
154
155
156
157
158
159
162
163
164
165
166
167
168
169
172
173
174
175
178
179
180
181
182
183
185
186
187
188
190
191
192
193
195
196
197
198
200
201
202
203
204
206
207
208
209
211
212
213
214
216void QHttp2Stream::finishWithError(Http2::Http2Error errorCode,
const QString &message)
218 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u finished with error: %ls (error code: %u)",
219 getConnection(), m_streamID, qUtf16Printable(message), errorCode);
220 transitionState(StateTransition::RST);
221 emit errorOccurred(errorCode, message);
224void QHttp2Stream::finishWithError(Http2::Http2Error errorCode)
226 QNetworkReply::NetworkError ignored = QNetworkReply::NoError;
228 qt_error(errorCode, ignored, message);
229 finishWithError(errorCode, message);
233void QHttp2Stream::streamError(Http2::Http2Error errorCode,
234 QLatin1StringView message)
236 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u finished with error: %ls (error code: %u)",
237 getConnection(), m_streamID, qUtf16Printable(message), errorCode);
239 sendRST_STREAM(errorCode);
240 emit errorOccurred(errorCode, message);
244
245
246
247
248
249
250bool QHttp2Stream::sendRST_STREAM(Http2::Http2Error errorCode)
252 if (m_state == State::Closed || m_state == State::Idle)
255 if (m_RST_STREAM_received.has_value())
258 getConnection()->registerStreamAsResetLocally(streamID());
260 m_RST_STREAM_sent = errorCode;
261 qCDebug(qHttp2ConnectionLog,
"[%p] sending RST_STREAM on stream %u, code: %u", getConnection(),
262 m_streamID, errorCode);
263 transitionState(StateTransition::RST);
265 QHttp2Connection *connection = getConnection();
266 FrameWriter &frameWriter = connection->frameWriter;
267 frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, m_streamID);
268 frameWriter.append(quint32(errorCode));
269 return frameWriter.write(*connection->getSocket());
273
274
275
276
277
278
279
280
281
282
283
284bool QHttp2Stream::sendDATA(
const QByteArray &payload,
bool endStream)
286 Q_ASSERT(!m_uploadByteDevice);
287 if (m_state != State::Open && m_state != State::HalfClosedRemote)
290 auto *byteDevice = QNonContiguousByteDeviceFactory::create(payload);
291 m_owningByteDevice =
true;
292 byteDevice->setParent(
this);
293 return sendDATA(byteDevice, endStream);
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311bool QHttp2Stream::sendDATA(QIODevice *device,
bool endStream)
313 Q_ASSERT(!m_uploadDevice);
314 Q_ASSERT(!m_uploadByteDevice);
316 if (m_state != State::Open && m_state != State::HalfClosedRemote) {
317 qCWarning(qHttp2ConnectionLog,
"[%p] attempt to sendDATA on closed stream %u, "
319 getConnection(), m_streamID, device);
323 qCDebug(qHttp2ConnectionLog,
"[%p] starting sendDATA on stream %u, of device: %p",
324 getConnection(), m_streamID, device);
325 auto *byteDevice = QNonContiguousByteDeviceFactory::create(device);
326 m_owningByteDevice =
true;
327 byteDevice->setParent(
this);
328 m_uploadDevice = device;
329 return sendDATA(byteDevice, endStream);
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347bool QHttp2Stream::sendDATA(QNonContiguousByteDevice *device,
bool endStream)
349 Q_ASSERT(!m_uploadByteDevice);
351 if (m_state != State::Open && m_state != State::HalfClosedRemote) {
352 qCWarning(qHttp2ConnectionLog,
"[%p] attempt to sendDATA on closed stream %u, "
354 getConnection(), m_streamID, device);
358 qCDebug(qHttp2ConnectionLog,
"[%p] starting sendDATA on stream %u, of device: %p",
359 getConnection(), m_streamID, device);
360 m_uploadByteDevice = device;
361 m_endStreamAfterDATA = endStream;
362 connect(m_uploadByteDevice, &QNonContiguousByteDevice::readyRead,
this,
363 &QHttp2Stream::maybeResumeUpload);
364 connect(m_uploadByteDevice, &QObject::destroyed,
this, &QHttp2Stream::uploadDeviceDestroyed);
372void QHttp2Stream::internalSendDATA()
374 Q_ASSERT(m_uploadByteDevice);
375 QHttp2Connection *connection = getConnection();
376 Q_ASSERT(connection->maxFrameSize > frameHeaderSize);
377 QIODevice *socket = connection->getSocket();
379 qCDebug(qHttp2ConnectionLog,
380 "[%p] stream %u, about to write to socket, current session window size: %d, stream "
381 "window size: %d, bytes available: %lld",
382 connection, m_streamID, connection->sessionSendWindowSize, m_sendWindow,
383 m_uploadByteDevice->size() - m_uploadByteDevice->pos());
385 qint32 remainingWindowSize = std::min<qint32>(connection->sessionSendWindowSize, m_sendWindow);
386 FrameWriter &frameWriter = connection->frameWriter;
387 qint64 totalBytesWritten = 0;
388 const auto deviceCanRead = [
this, connection] {
393 const qint64 requestSize = connection->maxFrameSize * 10ll;
395 return m_uploadByteDevice->readPointer(requestSize, tmp) !=
nullptr && tmp > 0;
398 bool sentEND_STREAM =
false;
399 while (remainingWindowSize && deviceCanRead()) {
400 quint32 bytesWritten = 0;
401 qint32 remainingBytesInFrame = qint32(connection->maxFrameSize);
402 frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, streamID());
404 while (remainingWindowSize && deviceCanRead() && remainingBytesInFrame) {
405 const qint32 maxToWrite = std::min(remainingWindowSize, remainingBytesInFrame);
407 qint64 outBytesAvail = 0;
408 const char *readPointer = m_uploadByteDevice->readPointer(maxToWrite, outBytesAvail);
409 if (!readPointer || outBytesAvail <= 0) {
410 qCDebug(qHttp2ConnectionLog,
411 "[%p] stream %u, cannot write data, device (%p) has %lld bytes available",
412 connection, m_streamID, m_uploadByteDevice, outBytesAvail);
415 const qint32 bytesToWrite = qint32(std::min<qint64>(maxToWrite, outBytesAvail));
416 frameWriter.append(QByteArrayView(readPointer, bytesToWrite));
417 m_uploadByteDevice->advanceReadPointer(bytesToWrite);
419 bytesWritten += bytesToWrite;
421 m_sendWindow -= bytesToWrite;
422 Q_ASSERT(m_sendWindow >= 0);
423 connection->sessionSendWindowSize -= bytesToWrite;
424 Q_ASSERT(connection->sessionSendWindowSize >= 0);
425 remainingBytesInFrame -= bytesToWrite;
426 Q_ASSERT(remainingBytesInFrame >= 0);
427 remainingWindowSize -= bytesToWrite;
428 Q_ASSERT(remainingWindowSize >= 0);
431 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, writing %u bytes to socket", connection,
432 m_streamID, bytesWritten);
433 if (!deviceCanRead() && m_uploadByteDevice->atEnd() && m_endStreamAfterDATA) {
434 sentEND_STREAM =
true;
435 frameWriter.addFlag(FrameFlag::END_STREAM);
437 if (!frameWriter.write(*socket)) {
438 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, failed to write to socket", connection,
440 return finishWithError(INTERNAL_ERROR,
"failed to write to socket"_L1);
443 totalBytesWritten += bytesWritten;
446 qCDebug(qHttp2ConnectionLog,
447 "[%p] stream %u, wrote %lld bytes total, if the device is not exhausted, we'll write "
448 "more later. Remaining window size: %d",
449 connection, m_streamID, totalBytesWritten, remainingWindowSize);
451 emit bytesWritten(totalBytesWritten);
452 if (sentEND_STREAM || (!deviceCanRead() && m_uploadByteDevice->atEnd())) {
453 qCDebug(qHttp2ConnectionLog,
454 "[%p] stream %u, exhausted device %p, sent END_STREAM? %d, %ssending end stream "
456 connection, m_streamID, m_uploadByteDevice, sentEND_STREAM,
457 m_endStreamAfterDATA ?
"" :
"not ");
458 if (!sentEND_STREAM && m_endStreamAfterDATA) {
463 frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, streamID());
464 frameWriter.write(*socket);
467 }
else if (isUploadBlocked()) {
468 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, upload blocked", connection, m_streamID);
469 emit uploadBlocked();
473void QHttp2Stream::finishSendDATA()
475 if (m_endStreamAfterDATA)
476 transitionState(StateTransition::CloseLocal);
478 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
479 m_uploadDevice =
nullptr;
480 if (m_owningByteDevice) {
481 m_owningByteDevice =
false;
482 delete m_uploadByteDevice;
484 m_uploadByteDevice =
nullptr;
485 emit uploadFinished();
488void QHttp2Stream::maybeResumeUpload()
490 qCDebug(qHttp2ConnectionLog,
491 "[%p] stream %u, maybeResumeUpload. Upload device: %p, bytes available: %lld, blocked? "
493 getConnection(), m_streamID, m_uploadByteDevice,
494 !m_uploadByteDevice ? 0 : m_uploadByteDevice->size() - m_uploadByteDevice->pos(),
496 if (isUploadingDATA() && !isUploadBlocked())
499 getConnection()->m_blockedStreams.insert(streamID());
503
504
505
506bool QHttp2Stream::isUploadBlocked()
const noexcept
508 constexpr auto MinFrameSize = Http2::frameHeaderSize + 1;
509 return isUploadingDATA()
510 && (m_sendWindow <= MinFrameSize
511 || getConnection()->sessionSendWindowSize <= MinFrameSize);
514void QHttp2Stream::uploadDeviceReadChannelFinished()
520
521
522
523
524
525
526bool QHttp2Stream::sendHEADERS(
const HPack::HttpHeader &headers,
bool endStream, quint8 priority)
528 using namespace HPack;
529 if (
auto hs = header_size(headers);
530 !hs.first || hs.second > getConnection()->maxHeaderListSize()) {
534 transitionState(StateTransition::Open);
536 Q_ASSERT(m_state == State::Open || m_state == State::HalfClosedRemote);
538 QHttp2Connection *connection = getConnection();
540 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, sending HEADERS frame with %u entries",
541 connection, streamID(), uint(headers.size()));
543 QIODevice *socket = connection->getSocket();
544 FrameWriter &frameWriter = connection->frameWriter;
546 frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS, streamID());
548 frameWriter.addFlag(FrameFlag::END_STREAM);
550 frameWriter.append(quint32());
551 frameWriter.append(priority);
554 BitOStream outputStream(frameWriter.outboundFrame().buffer);
557 for (
auto &maybePendingTableSizeUpdate : connection->pendingTableSizeUpdates) {
558 if (!maybePendingTableSizeUpdate)
560 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, sending dynamic table size update of size %u",
561 connection, streamID(), *maybePendingTableSizeUpdate);
562 connection->encoder.setMaxDynamicTableSize(*maybePendingTableSizeUpdate);
563 connection->encoder.encodeSizeUpdate(outputStream, *maybePendingTableSizeUpdate);
564 maybePendingTableSizeUpdate.reset();
567 if (connection->m_connectionType == QHttp2Connection::Type::Client) {
568 if (!connection->encoder.encodeRequest(outputStream, headers))
571 if (!connection->encoder.encodeResponse(outputStream, headers))
575 bool result = frameWriter.writeHEADERS(*socket, connection->maxFrameSize);
577 transitionState(StateTransition::CloseLocal);
583
584
585
586
587void QHttp2Stream::sendWINDOW_UPDATE(quint32 delta)
589 QHttp2Connection *connection = getConnection();
590 m_recvWindow += qint32(delta);
591 connection->sendWINDOW_UPDATE(streamID(), delta);
594void QHttp2Stream::uploadDeviceDestroyed()
596 if (isUploadingDATA()) {
599 streamError(CANCEL, QLatin1String(
"Upload device destroyed while uploading"));
600 emit uploadDeviceError(
"Upload device destroyed while uploading"_L1);
602 m_uploadDevice =
nullptr;
605void QHttp2Stream::setState(State newState)
607 if (m_state == newState)
609 qCDebug(qHttp2ConnectionLog,
"[%p] stream %u, state changed from %d to %d", getConnection(),
610 streamID(),
int(m_state),
int(newState));
612 emit stateChanged(newState);
618void QHttp2Stream::transitionState(StateTransition transition)
622 if (transition == StateTransition::Open)
623 setState(State::Open);
628 switch (transition) {
629 case StateTransition::CloseLocal:
630 setState(State::HalfClosedLocal);
632 case StateTransition::CloseRemote:
633 setState(State::HalfClosedRemote);
635 case StateTransition::RST:
636 setState(State::Closed);
638 case StateTransition::Open:
642 case State::HalfClosedLocal:
643 if (transition == StateTransition::CloseRemote || transition == StateTransition::RST)
644 setState(State::Closed);
646 case State::HalfClosedRemote:
647 if (transition == StateTransition::CloseLocal || transition == StateTransition::RST)
648 setState(State::Closed);
650 case State::ReservedRemote:
651 if (transition == StateTransition::RST) {
652 setState(State::Closed);
653 }
else if (transition == StateTransition::CloseLocal) {
654 setState(State::HalfClosedLocal);
662void QHttp2Stream::handleDATA(
const Frame &inboundFrame)
664 QHttp2Connection *connection = getConnection();
666 qCDebug(qHttp2ConnectionLog,
667 "[%p] stream %u, received DATA frame with payload of %u bytes, closing stream? %s",
668 connection, m_streamID, inboundFrame.payloadSize(),
669 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
675 Q_ASSERT(state() != State::HalfClosedRemote && state() != State::Closed);
677 if (qint32(inboundFrame.payloadSize()) > m_recvWindow) {
678 qCDebug(qHttp2ConnectionLog,
679 "[%p] stream %u, received DATA frame with payload size %u, "
680 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
681 connection, m_streamID, inboundFrame.payloadSize(), m_recvWindow);
682 return streamError(FLOW_CONTROL_ERROR, QLatin1String(
"data bigger than window size"));
688 Q_ASSERT(inboundFrame.buffer.size() >= frameHeaderSize);
689 Q_ASSERT(inboundFrame.payloadSize() + frameHeaderSize == inboundFrame.buffer.size());
691 m_recvWindow -= qint32(inboundFrame.payloadSize());
692 const bool endStream = inboundFrame.flags().testFlag(FrameFlag::END_STREAM);
694 if (inboundFrame.dataSize() > 0 || endStream) {
695 QByteArray fragment(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()),
696 inboundFrame.dataSize());
698 transitionState(StateTransition::CloseRemote);
699 emit dataReceived(fragment, endStream);
700 m_downloadBuffer.append(std::move(fragment));
703 if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) {
705 sendWINDOW_UPDATE(quint32(connection->streamInitialReceiveWindowSize - m_recvWindow));
709void QHttp2Stream::handleHEADERS(Http2::FrameFlags frameFlags,
const HPack::HttpHeader &headers)
711 if (m_state == State::Idle)
712 transitionState(StateTransition::Open);
713 const bool endStream = frameFlags.testFlag(FrameFlag::END_STREAM);
715 transitionState(StateTransition::CloseRemote);
716 if (!headers.empty()) {
717 m_headers.insert(m_headers.end(), headers.begin(), headers.end());
718 emit headersUpdated();
720 emit headersReceived(headers, endStream);
723void QHttp2Stream::handleRST_STREAM(
const Frame &inboundFrame)
725 if (m_state == State::Closed)
728 transitionState(StateTransition::RST);
729 m_RST_STREAM_received = qFromBigEndian<quint32>(inboundFrame.dataBegin());
730 if (isUploadingDATA()) {
731 disconnect(m_uploadByteDevice,
nullptr,
this,
nullptr);
732 m_uploadDevice =
nullptr;
733 m_uploadByteDevice =
nullptr;
735 finishWithError(Http2Error(*m_RST_STREAM_received));
738void QHttp2Stream::handleWINDOW_UPDATE(
const Frame &inboundFrame)
740 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
741 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
743 if (!valid || qAddOverflow(m_sendWindow, qint32(delta), &sum)) {
744 qCDebug(qHttp2ConnectionLog,
745 "[%p] stream %u, received WINDOW_UPDATE frame with invalid delta %u, sending "
747 getConnection(), m_streamID, delta);
748 return streamError(PROTOCOL_ERROR,
"invalid WINDOW_UPDATE delta"_L1);
752 if (isUploadingDATA())
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
775
776
777
778
779
782
783
784
785
788
789
790
791
794
795
796
797
800
801
802
803
806
807
808
809
810
811
814
815
816
817
818
819
820
821
824
825
826
827
828
829
830QHttp2Connection *QHttp2Connection::createUpgradedConnection(QIODevice *socket,
831 const QHttp2Configuration &config)
835 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
836 connection->setH2Configuration(config);
837 connection->m_connectionType = QHttp2Connection::Type::Client;
838 connection->m_upgradedConnection =
true;
841 QHttp2Stream *stream = connection->createLocalStreamInternal().unwrap();
842 Q_ASSERT(stream->streamID() == 1);
843 stream->setState(QHttp2Stream::State::HalfClosedLocal);
845 if (!connection->m_prefaceSent)
848 return connection.release();
852
853
854
855
856
857QHttp2Connection *QHttp2Connection::createDirectConnection(QIODevice *socket,
858 const QHttp2Configuration &config)
860 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
861 connection->setH2Configuration(config);
862 connection->m_connectionType = QHttp2Connection::Type::Client;
864 return connection.release();
868
869
870
871
872QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *socket,
873 const QHttp2Configuration &config)
875 auto connection = std::unique_ptr<QHttp2Connection>(
new QHttp2Connection(socket));
876 connection->setH2Configuration(config);
877 connection->m_connectionType = QHttp2Connection::Type::Server;
879 connection->m_nextStreamID = 2;
881 connection->m_waitingForClientPreface =
true;
883 return connection.release();
887
888
889
890
891
892
893
894QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> QHttp2Connection::createStream()
896 Q_ASSERT(m_connectionType == Type::Client);
897 if (m_nextStreamID > lastValidStreamID)
898 return { QHttp2Connection::CreateStreamError::StreamIdsExhausted };
899 return createLocalStreamInternal();
902QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
903QHttp2Connection::createLocalStreamInternal()
906 return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY };
907 const quint32 streamID = m_nextStreamID;
908 if (size_t(m_peerMaxConcurrentStreams) <= size_t(numActiveLocalStreams()))
909 return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached };
911 if (QHttp2Stream *ptr = createStreamInternal_impl(streamID)) {
916 return { QHttp2Connection::CreateStreamError::UnknownError };
919QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID)
921 Q_ASSERT(streamID > m_lastIncomingStreamID || streamID >= m_nextStreamID);
923 if (m_connectionType == Type::Client && !m_prefaceSent && !sendClientPreface()) {
924 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send client preface",
this);
928 auto result = m_streams.tryEmplace(streamID,
nullptr);
929 if (!result.inserted)
931 QPointer<QHttp2Stream> &stream = result.iterator.value();
932 stream =
new QHttp2Stream(
this, streamID);
933 stream->m_recvWindow = streamInitialReceiveWindowSize;
934 stream->m_sendWindow = streamInitialSendWindowSize;
936 connect(stream, &QHttp2Stream::uploadBlocked,
this, [
this, stream] {
937 m_blockedStreams.insert(stream->streamID());
939 *result.iterator = stream;
940 return *result.iterator;
943qsizetype QHttp2Connection::numActiveStreamsImpl(quint32 mask)
const noexcept
945 const auto shouldCount = [mask](
const QPointer<QHttp2Stream> &stream) ->
bool {
946 return stream && (stream->streamID() & 1) == mask && stream->isActive();
948 return std::count_if(m_streams.cbegin(), m_streams.cend(), shouldCount);
952
953
954
955qsizetype QHttp2Connection::numActiveRemoteStreams()
const noexcept
957 const quint32 RemoteMask = m_connectionType == Type::Client ? 0 : 1;
958 return numActiveStreamsImpl(RemoteMask);
962
963
964
965qsizetype QHttp2Connection::numActiveLocalStreams()
const noexcept
967 const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
968 return numActiveStreamsImpl(LocalMask);
972
973
974
975QHttp2Stream *QHttp2Connection::getStream(quint32 streamID)
const
977 return m_streams.value(streamID,
nullptr).get();
982
983
984
985
986
989
990
991
992
993
996
997
998
999
1000
1003
1004
1005
1006
1007
1008
1011
1012
1013
1014
1017
1018
1019
1020
1021
1023QHttp2Connection::QHttp2Connection(QIODevice *socket) : QObject(socket)
1026 Q_ASSERT(socket->isOpen());
1027 Q_ASSERT(socket->openMode() & QIODevice::ReadWrite);
1034QHttp2Connection::~QHttp2Connection()
1038 for (QPointer<QHttp2Stream> &stream : std::exchange(m_streams, {}))
1039 delete stream.get();
1042bool QHttp2Connection::serverCheckClientPreface()
1044 if (!m_waitingForClientPreface)
1046 auto *socket = getSocket();
1047 if (socket->bytesAvailable() < Http2::clientPrefaceLength)
1049 if (!readClientPreface()) {
1051 emit errorOccurred(Http2Error::PROTOCOL_ERROR,
"invalid client preface"_L1);
1052 qCDebug(qHttp2ConnectionLog,
"[%p] Invalid client preface",
this);
1055 qCDebug(qHttp2ConnectionLog,
"[%p] Peer sent valid client preface",
this);
1056 m_waitingForClientPreface =
false;
1057 if (!sendServerPreface()) {
1058 connectionError(INTERNAL_ERROR,
"Failed to send server preface");
1064bool QHttp2Connection::sendPing()
1066 std::array<
char, 8> data;
1068 QRandomGenerator gen;
1069 gen.generate(data.begin(), data.end());
1070 return sendPing(data);
1073bool QHttp2Connection::sendPing(QByteArrayView data)
1075 frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
1077 Q_ASSERT(data.length() == 8);
1078 if (!m_lastPingSignature) {
1079 m_lastPingSignature = data.toByteArray();
1081 qCWarning(qHttp2ConnectionLog,
"[%p] No PING is sent while waiting for the previous PING.",
this);
1085 frameWriter.append((uchar*)data.data(), (uchar*)data.end());
1086 frameWriter.write(*getSocket());
1091
1092
1093
1094
1095void QHttp2Connection::handleReadyRead()
1098 if (m_connectionType == Type::Server && !serverCheckClientPreface())
1101 const auto streamIsActive = [](
const QPointer<QHttp2Stream> &stream) {
1102 return stream && stream->isActive();
1104 if (m_goingAway && std::none_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
1108 QIODevice *socket = getSocket();
1110 qCDebug(qHttp2ConnectionLog,
"[%p] Receiving data, %lld bytes available",
this,
1111 socket->bytesAvailable());
1113 using namespace Http2;
1117 while (!m_goingAway || std::any_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
1118 const auto result = frameReader.read(*socket);
1119 if (result != FrameStatus::goodFrame)
1120 qCDebug(qHttp2ConnectionLog,
"[%p] Tried to read frame, got %d",
this,
int(result));
1122 case FrameStatus::incompleteFrame:
1124 case FrameStatus::protocolError:
1125 return connectionError(PROTOCOL_ERROR,
"invalid frame");
1126 case FrameStatus::sizeError: {
1127 const auto streamID = frameReader.inboundFrame().streamID();
1128 const auto frameType = frameReader.inboundFrame().type();
1129 auto stream = getStream(streamID);
1135 if (frameType == FrameType::HEADERS ||
1136 frameType == FrameType::SETTINGS ||
1137 frameType == FrameType::PUSH_PROMISE ||
1138 frameType == FrameType::CONTINUATION ||
1140 frameType == FrameType::RST_STREAM ||
1141 streamID == connectionStreamID)
1142 return connectionError(FRAME_SIZE_ERROR,
"invalid frame size");
1145 return stream->streamError(Http2Error::FRAME_SIZE_ERROR,
1146 QLatin1String(
"invalid frame size"));
1154 Q_ASSERT(result == FrameStatus::goodFrame);
1156 inboundFrame = std::move(frameReader.inboundFrame());
1158 const auto frameType = inboundFrame.type();
1159 qCDebug(qHttp2ConnectionLog,
"[%p] Successfully read a frame, with type: %d",
this,
1166 if (continuationExpected && frameType != FrameType::CONTINUATION)
1167 return connectionError(PROTOCOL_ERROR,
"CONTINUATION expected");
1169 switch (frameType) {
1170 case FrameType::DATA:
1173 case FrameType::HEADERS:
1176 case FrameType::PRIORITY:
1179 case FrameType::RST_STREAM:
1182 case FrameType::SETTINGS:
1185 case FrameType::PUSH_PROMISE:
1186 handlePUSH_PROMISE();
1188 case FrameType::PING:
1191 case FrameType::GOAWAY:
1194 case FrameType::WINDOW_UPDATE:
1195 handleWINDOW_UPDATE();
1197 case FrameType::CONTINUATION:
1198 handleCONTINUATION();
1200 case FrameType::LAST_FRAME_TYPE:
1207bool QHttp2Connection::readClientPreface()
1209 auto *socket = getSocket();
1210 Q_ASSERT(socket->bytesAvailable() >= Http2::clientPrefaceLength);
1211 char buffer[Http2::clientPrefaceLength];
1212 const qint64 read = socket->read(buffer, Http2::clientPrefaceLength);
1213 if (read != Http2::clientPrefaceLength)
1215 return memcmp(buffer, Http2::Http2clientPreface, Http2::clientPrefaceLength) == 0;
1219
1220
1221
1222void QHttp2Connection::handleConnectionClosure()
1224 const auto errorString = QCoreApplication::translate(
"QHttp",
"Connection closed");
1225 for (
auto it = m_streams.cbegin(), end = m_streams.cend(); it != end; ++it) {
1226 const QPointer<QHttp2Stream> &stream = it.value();
1227 if (stream && stream->isActive())
1228 stream->finishWithError(PROTOCOL_ERROR, errorString);
1232void QHttp2Connection::setH2Configuration(QHttp2Configuration config)
1234 m_config = std::move(config);
1237 maxSessionReceiveWindowSize = qint32(m_config.sessionReceiveWindowSize());
1238 pushPromiseEnabled = m_config.serverPushEnabled();
1239 streamInitialReceiveWindowSize = qint32(m_config.streamReceiveWindowSize());
1240 m_maxConcurrentStreams = m_config.maxConcurrentStreams();
1241 encoder.setCompressStrings(m_config.huffmanCompressionEnabled());
1244void QHttp2Connection::connectionError(Http2Error errorCode,
const char *message)
1252 qCCritical(qHttp2ConnectionLog,
"[%p] Connection error: %s (%d)",
this, message,
1258 sendGOAWAY(errorCode);
1259 auto messageView = QLatin1StringView(message);
1261 for (QHttp2Stream *stream : std::as_const(m_streams)) {
1262 if (stream && stream->isActive())
1263 stream->finishWithError(errorCode, messageView);
1269void QHttp2Connection::closeSession()
1271 emit connectionClosed();
1274bool QHttp2Connection::streamWasResetLocally(quint32 streamID)
noexcept
1276 return m_resetStreamIDs.contains(streamID);
1279void QHttp2Connection::registerStreamAsResetLocally(quint32 streamID)
1287 m_resetStreamIDs.append(streamID);
1288 while (m_resetStreamIDs.size() > 100)
1289 m_resetStreamIDs.takeFirst();
1292bool QHttp2Connection::isInvalidStream(quint32 streamID)
noexcept
1294 auto stream = m_streams.value(streamID,
nullptr);
1295 return (!stream || stream->wasResetbyPeer()) && !streamWasResetLocally(streamID);
1298bool QHttp2Connection::sendClientPreface()
1300 QIODevice *socket = getSocket();
1302 const qint64 written = socket->write(Http2clientPreface, clientPrefaceLength);
1303 if (written != clientPrefaceLength)
1306 if (!sendSETTINGS()) {
1307 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send SETTINGS",
this);
1310 m_prefaceSent =
true;
1311 if (socket->bytesAvailable())
1312 QMetaObject::invokeMethod(
this, &QHttp2Connection::handleReadyRead, Qt::QueuedConnection);
1316bool QHttp2Connection::sendServerPreface()
1320 if (!sendSETTINGS()) {
1321 qCWarning(qHttp2ConnectionLog,
"[%p] Failed to send SETTINGS",
this);
1324 m_prefaceSent =
true;
1328bool QHttp2Connection::sendSETTINGS()
1330 QIODevice *socket = getSocket();
1332 frameWriter.setOutboundFrame(configurationToSettingsFrame(m_config));
1333 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS frame, %d bytes",
this,
1334 frameWriter.outboundFrame().payloadSize());
1335 Q_ASSERT(frameWriter.outboundFrame().payloadSize());
1337 if (!frameWriter.write(*socket))
1340 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1343 const auto delta = maxSessionReceiveWindowSize - defaultSessionWindowSize;
1344 if (delta && !sendWINDOW_UPDATE(connectionStreamID, delta))
1347 waitingForSettingsACK =
true;
1351bool QHttp2Connection::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
1353 qCDebug(qHttp2ConnectionLog,
"[%p] Sending WINDOW_UPDATE frame, stream %d, delta %u",
this,
1355 frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
1356 frameWriter.append(delta);
1357 return frameWriter.write(*getSocket());
1360bool QHttp2Connection::sendGOAWAY(Http2::Http2Error errorCode)
1362 frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY,
1363 Http2PredefinedParameters::connectionStreamID);
1364 frameWriter.append(quint32(m_lastIncomingStreamID));
1365 frameWriter.append(quint32(errorCode));
1366 return frameWriter.write(*getSocket());
1369bool QHttp2Connection::sendSETTINGS_ACK()
1371 frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
1372 return frameWriter.write(*getSocket());
1375void QHttp2Connection::handleDATA()
1377 Q_ASSERT(inboundFrame.type() == FrameType::DATA);
1379 const auto streamID = inboundFrame.streamID();
1383 if (streamID == connectionStreamID)
1384 return connectionError(PROTOCOL_ERROR,
"DATA on the connection stream");
1386 if (isInvalidStream(streamID))
1387 return connectionError(ENHANCE_YOUR_CALM,
"DATA on invalid stream");
1389 QHttp2Stream *stream =
nullptr;
1390 if (!streamWasResetLocally(streamID)) {
1391 stream = getStream(streamID);
1394 if (stream->state() == QHttp2Stream::State::HalfClosedRemote
1395 || stream->state() == QHttp2Stream::State::Closed) {
1396 return stream->streamError(Http2Error::STREAM_CLOSED,
1397 QLatin1String(
"Data on closed stream"));
1401 if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
1402 qCDebug(qHttp2ConnectionLog,
1403 "[%p] Received DATA frame with payload size %u, "
1404 "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
1405 this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
1406 return connectionError(FLOW_CONTROL_ERROR,
"Flow control error");
1409 sessionReceiveWindowSize -= inboundFrame.payloadSize();
1412 stream->handleDATA(inboundFrame);
1414 if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM))
1415 emit receivedEND_STREAM(streamID);
1417 if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
1419 QMetaObject::invokeMethod(
this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
1420 quint32(connectionStreamID),
1421 quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
1422 sessionReceiveWindowSize = maxSessionReceiveWindowSize;
1426void QHttp2Connection::handleHEADERS()
1428 Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
1430 const auto streamID = inboundFrame.streamID();
1431 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS frame on stream %d, end stream? %s",
this,
1432 streamID, inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
1436 if (streamID == connectionStreamID)
1437 return connectionError(PROTOCOL_ERROR,
"HEADERS on 0x0 stream");
1439 const bool isClient = m_connectionType == Type::Client;
1440 const bool isClientInitiatedStream = !!(streamID & 1);
1441 const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
1443 if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
1444 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1445 QHttp2Stream *newStream = createStreamInternal_impl(streamID);
1446 Q_ASSERT(newStream);
1447 m_lastIncomingStreamID = streamID;
1449 if (!streamCountIsOk) {
1450 newStream->setState(QHttp2Stream::State::Open);
1451 newStream->streamError(PROTOCOL_ERROR, QLatin1String(
"Max concurrent streams reached"));
1453 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1457 qCDebug(qHttp2ConnectionLog,
"[%p] Created new incoming stream %d",
this, streamID);
1458 emit newIncomingStream(newStream);
1459 }
else if (streamWasResetLocally(streamID)) {
1460 qCDebug(qHttp2ConnectionLog,
1461 "[%p] Received HEADERS on previously locally reset stream %d (must process but ignore)",
1464 }
else if (
auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
1467 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on non-existent stream %d",
this,
1469 return connectionError(PROTOCOL_ERROR,
"HEADERS on invalid stream");
1470 }
else if (isInvalidStream(streamID)) {
1473 qCDebug(qHttp2ConnectionLog,
"[%p] Received HEADERS on reset stream %d",
this, streamID);
1474 return connectionError(ENHANCE_YOUR_CALM,
"HEADERS on invalid stream");
1477 const auto flags = inboundFrame.flags();
1478 if (flags.testFlag(FrameFlag::PRIORITY)) {
1479 qCDebug(qHttp2ConnectionLog,
"[%p] HEADERS frame on stream %d has PRIORITY flag",
this,
1486 const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
1487 continuedFrames.clear();
1488 continuedFrames.push_back(std::move(inboundFrame));
1490 continuationExpected =
true;
1494 handleContinuedHEADERS();
1497void QHttp2Connection::handlePRIORITY()
1499 Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
1500 || inboundFrame.type() == FrameType::HEADERS);
1502 const auto streamID = inboundFrame.streamID();
1505 if (streamID == connectionStreamID)
1506 return connectionError(PROTOCOL_ERROR,
"PRIORITY on 0x0 stream");
1510 if (isInvalidStream(streamID))
1511 return connectionError(ENHANCE_YOUR_CALM,
"PRIORITY on invalid stream");
1516 Q_ASSERT(inboundFrame.type() != FrameType::PRIORITY || inboundFrame.payloadSize() == 5);
1518 quint32 streamDependency = 0;
1520 const bool noErr = inboundFrame.priority(&streamDependency, &weight);
1524 const bool exclusive = streamDependency & 0x80000000;
1525 streamDependency &= ~0x80000000;
1529 Q_UNUSED(exclusive);
1533void QHttp2Connection::handleRST_STREAM()
1535 Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
1541 const auto streamID = inboundFrame.streamID();
1542 if (streamID == connectionStreamID)
1543 return connectionError(PROTOCOL_ERROR,
"RST_STREAM on 0x0");
1548 Q_ASSERT(inboundFrame.payloadSize() == 4);
1550 const auto error = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1551 if (QPointer<QHttp2Stream> stream = m_streams[streamID])
1552 emit stream->rstFrameReceived(error);
1555 const quint32 lastRelevantStreamID = [
this, streamID]() {
1556 quint32 peerMask = m_connectionType == Type::Client ? 0 : 1;
1557 return ((streamID & 1) == peerMask) ? m_lastIncomingStreamID : m_nextStreamID - 2;
1559 if (streamID > lastRelevantStreamID) {
1563 return connectionError(PROTOCOL_ERROR,
"RST_STREAM on idle stream");
1566 Q_ASSERT(inboundFrame.dataSize() == 4);
1568 if (QPointer<QHttp2Stream> stream = m_streams[streamID])
1569 stream->handleRST_STREAM(inboundFrame);
1572void QHttp2Connection::handleSETTINGS()
1575 Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
1579 if (inboundFrame.streamID() != connectionStreamID)
1580 return connectionError(PROTOCOL_ERROR,
"SETTINGS on invalid stream");
1582 if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
1585 if (inboundFrame.payloadSize ())
1586 return connectionError(FRAME_SIZE_ERROR,
"SETTINGS ACK with data");
1587 if (!waitingForSettingsACK)
1588 return connectionError(PROTOCOL_ERROR,
"unexpected SETTINGS ACK");
1589 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ACK",
this);
1590 waitingForSettingsACK =
false;
1593 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS frame",
this);
1595 if (inboundFrame.dataSize()) {
1599 Q_ASSERT (inboundFrame.payloadSize() % 6 == 0);
1601 auto src = inboundFrame.dataBegin();
1602 for (
const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
1603 const Settings identifier = Settings(qFromBigEndian<quint16>(src));
1604 const quint32 intVal = qFromBigEndian<quint32>(src + 2);
1605 if (!acceptSetting(identifier, intVal)) {
1607 qCDebug(qHttp2ConnectionLog,
"[%p] Received an unacceptable setting, %u, %u",
this,
1608 quint32(identifier), intVal);
1614 qCDebug(qHttp2ConnectionLog,
"[%p] Sending SETTINGS ACK",
this);
1616 emit settingsFrameReceived();
1619void QHttp2Connection::handlePUSH_PROMISE()
1622 Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
1627 if (!pushPromiseEnabled && !waitingForSettingsACK) {
1630 return connectionError(PROTOCOL_ERROR,
"unexpected PUSH_PROMISE frame");
1635 const auto streamID = inboundFrame.streamID();
1636 if (streamID == connectionStreamID)
1637 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid associated stream (0x0)");
1639 auto it = m_streams.constFind(streamID);
1641 if (it != m_streams.constEnd()) {
1642 QHttp2Stream *associatedStream = it->get();
1643 if (associatedStream->state() != QHttp2Stream::State::Open
1644 && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
1646 it = m_streams.constEnd();
1656 if (it == m_streams.constEnd())
1657 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1658 if ((m_connectionType == Type::Client && (streamID & 1) == 0) ||
1659 (m_connectionType == Type::Server && (streamID & 1) == 1)) {
1660 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1662 if ((*it)->state() != QHttp2Stream::State::Open &&
1663 (*it)->state() != QHttp2Stream::State::HalfClosedLocal) {
1664 return connectionError(ENHANCE_YOUR_CALM,
"PUSH_PROMISE with invalid associated stream");
1669 const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1670 if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
1671 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with invalid promised stream ID");
1673 bool streamCountIsOk = size_t(m_maxConcurrentStreams) > size_t(numActiveRemoteStreams());
1676 auto *stream = createStreamInternal_impl(reservedID);
1678 return connectionError(PROTOCOL_ERROR,
"PUSH_PROMISE with already active stream ID");
1679 m_lastIncomingStreamID = reservedID;
1680 stream->setState(QHttp2Stream::State::ReservedRemote);
1682 if (!streamCountIsOk) {
1683 stream->streamError(PROTOCOL_ERROR, QLatin1String(
"Max concurrent streams reached"));
1684 emit incomingStreamErrorOccured(CreateStreamError::MaxConcurrentStreamsReached);
1690 if (!pushPromiseEnabled) {
1691 return stream->streamError(REFUSE_STREAM,
1692 QLatin1String(
"PUSH_PROMISE not enabled but ignored"));
1699 Q_ASSERT(inboundFrame.dataSize() > inboundFrame.padding());
1700 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
1701 continuedFrames.clear();
1702 continuedFrames.push_back(std::move(inboundFrame));
1705 continuationExpected =
true;
1709 handleContinuedHEADERS();
1712void QHttp2Connection::handlePING()
1714 Q_ASSERT(inboundFrame.type() == FrameType::PING);
1719 if (inboundFrame.streamID() != connectionStreamID)
1720 return connectionError(PROTOCOL_ERROR,
"PING on invalid stream");
1725 Q_ASSERT(inboundFrame.payloadSize() == 8);
1727 if (inboundFrame.flags() & FrameFlag::ACK) {
1728 QByteArrayView pingSignature(
reinterpret_cast<
const char *>(inboundFrame.dataBegin()), 8);
1729 if (!m_lastPingSignature.has_value()) {
1730 emit pingFrameReceived(PingState::PongNoPingSent);
1731 qCWarning(qHttp2ConnectionLog,
"[%p] PING with ACK received but no PING was sent.",
this);
1732 }
else if (pingSignature != m_lastPingSignature) {
1733 emit pingFrameReceived(PingState::PongSignatureChanged);
1734 qCWarning(qHttp2ConnectionLog,
"[%p] PING signature does not match the last PING.",
this);
1736 emit pingFrameReceived(PingState::PongSignatureIdentical);
1738 m_lastPingSignature.reset();
1741 emit pingFrameReceived(PingState::Ping);
1746 frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
1747 frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
1748 frameWriter.write(*getSocket());
1751void QHttp2Connection::handleGOAWAY()
1755 Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
1758 if (inboundFrame.streamID() != connectionStreamID)
1759 return connectionError(PROTOCOL_ERROR,
"GOAWAY on invalid stream");
1764 Q_ASSERT(inboundFrame.payloadSize() >= 8);
1766 const uchar *
const src = inboundFrame.dataBegin();
1767 quint32 lastStreamID = qFromBigEndian<quint32>(src);
1768 const Http2Error errorCode = Http2Error(qFromBigEndian<quint32>(src + 4));
1770 if (!lastStreamID) {
1774 }
else if (!(lastStreamID & 0x1)) {
1776 return connectionError(PROTOCOL_ERROR,
"GOAWAY with invalid last stream ID");
1777 }
else if (lastStreamID >= m_nextStreamID) {
1781 if (lastStreamID != lastValidStreamID || errorCode != HTTP2_NO_ERROR)
1782 return connectionError(PROTOCOL_ERROR,
"GOAWAY invalid stream/error code");
1789 emit receivedGOAWAY(errorCode, lastStreamID);
1791 for (quint32 id = lastStreamID; id < m_nextStreamID; id += 2) {
1792 QHttp2Stream *stream = m_streams.value(id,
nullptr);
1793 if (stream && stream->isActive())
1794 stream->finishWithError(errorCode,
"Received GOAWAY"_L1);
1797 const auto isActive = [](
const QHttp2Stream *stream) {
return stream && stream->isActive(); };
1798 if (std::none_of(m_streams.cbegin(), m_streams.cend(), isActive))
1802void QHttp2Connection::handleWINDOW_UPDATE()
1804 Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
1806 const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
1810 const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
1811 const auto streamID = inboundFrame.streamID();
1817 Q_ASSERT(inboundFrame.payloadSize() == 4);
1819 qCDebug(qHttp2ConnectionLog(),
"[%p] Received WINDOW_UPDATE, stream %d, delta %d",
this,
1821 if (streamID == connectionStreamID) {
1823 if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
1824 return connectionError(PROTOCOL_ERROR,
"WINDOW_UPDATE invalid delta");
1825 sessionSendWindowSize = sum;
1828 const auto blockedStreams = std::exchange(m_blockedStreams, {});
1829 for (quint32 blockedStreamID : blockedStreams) {
1830 const QPointer<QHttp2Stream> stream = m_streams.value(blockedStreamID);
1831 if (!stream || !stream->isActive())
1833 if (stream->isUploadingDATA() && !stream->isUploadBlocked())
1834 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
1835 Qt::QueuedConnection);
1838 QHttp2Stream *stream = m_streams.value(streamID);
1839 if (!stream || !stream->isActive()) {
1841 qCDebug(qHttp2ConnectionLog,
"[%p] Received WINDOW_UPDATE on closed stream %d",
this,
1844 }
else if (!valid) {
1845 return stream->streamError(PROTOCOL_ERROR,
1846 QLatin1String(
"WINDOW_UPDATE invalid delta"));
1848 stream->handleWINDOW_UPDATE(inboundFrame);
1852void QHttp2Connection::handleCONTINUATION()
1854 Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
1855 auto streamID = inboundFrame.streamID();
1856 qCDebug(qHttp2ConnectionLog,
1857 "[%p] Received CONTINUATION frame on stream %d, end stream? %s",
this, streamID,
1858 inboundFrame.flags().testFlag(Http2::FrameFlag::END_STREAM) ?
"yes" :
"no");
1859 if (continuedFrames.empty())
1860 return connectionError(PROTOCOL_ERROR,
1861 "CONTINUATION without a preceding HEADERS or PUSH_PROMISE");
1862 if (!continuationExpected)
1863 return connectionError(PROTOCOL_ERROR,
1864 "CONTINUATION after a frame with the END_HEADERS flag set");
1866 if (inboundFrame.streamID() != continuedFrames.front().streamID())
1867 return connectionError(PROTOCOL_ERROR,
"CONTINUATION on invalid stream");
1869 const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
1870 continuedFrames.push_back(std::move(inboundFrame));
1875 continuationExpected =
false;
1876 handleContinuedHEADERS();
1879void QHttp2Connection::handleContinuedHEADERS()
1884 Q_ASSERT(!continuedFrames.empty());
1885 const auto firstFrameType = continuedFrames[0].type();
1886 Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
1888 const auto streamID = continuedFrames[0].streamID();
1890 const auto streamIt = m_streams.constFind(streamID);
1891 if (firstFrameType == FrameType::HEADERS) {
1892 if (streamIt != m_streams.cend() && !streamWasResetLocally(streamID)) {
1893 QHttp2Stream *stream = streamIt.value();
1894 if (stream->state() != QHttp2Stream::State::HalfClosedLocal
1895 && stream->state() != QHttp2Stream::State::ReservedRemote
1896 && stream->state() != QHttp2Stream::State::Idle
1897 && stream->state() != QHttp2Stream::State::Open) {
1901 return stream->streamError(PROTOCOL_ERROR,
"HEADERS on invalid stream"_L1);
1909 std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
1910 const bool hasHeaderFields = !hpackBlock.empty();
1911 if (hasHeaderFields) {
1912 HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
1913 if (!decoder.decodeHeaderFields(inputStream))
1914 return connectionError(COMPRESSION_ERROR,
"HPACK decompression failed");
1916 if (firstFrameType == FrameType::PUSH_PROMISE) {
1925 if (streamIt != m_streams.cend()) {
1926 (*streamIt)->streamError(PROTOCOL_ERROR,
1927 QLatin1String(
"PUSH_PROMISE with incomplete headers"));
1933 constexpr auto hpackBlockHasContent = [](
const auto &c) {
return c.hpackBlockSize() > 0; };
1934 const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
1935 hpackBlockHasContent);
1937 return connectionError(FRAME_SIZE_ERROR,
"HEADERS frame too large");
1940 if (streamWasResetLocally(streamID) || streamIt == m_streams.cend())
1943 switch (firstFrameType) {
1944 case FrameType::HEADERS:
1945 streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
1947 case FrameType::PUSH_PROMISE: {
1948 std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
1951 if (m_promisedStreams.contains(*promiseKey))
1953 const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
1954 QHttp2Stream *stream = m_streams.value(promiseID);
1955 stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
1956 stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
1957 emit newPromisedStream(stream);
1958 m_promisedStreams.emplace(*promiseKey, promiseID);
1966bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
1968 switch (identifier) {
1969 case Settings::HEADER_TABLE_SIZE_ID: {
1970 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS HEADER_TABLE_SIZE %d",
this, newValue);
1971 if (newValue > maxAcceptableTableSize) {
1972 connectionError(PROTOCOL_ERROR,
"SETTINGS invalid table size");
1975 if (!pendingTableSizeUpdates[0] && encoder.dynamicTableCapacity() == newValue) {
1976 qCDebug(qHttp2ConnectionLog,
1977 "[%p] Ignoring SETTINGS HEADER_TABLE_SIZE %d (same as current value)",
this,
1982 if (pendingTableSizeUpdates[0].value_or(std::numeric_limits<quint32>::max()) >= newValue) {
1983 pendingTableSizeUpdates[0] = newValue;
1984 pendingTableSizeUpdates[1].reset();
1985 qCDebug(qHttp2ConnectionLog,
"[%p] Pending table size update to %u",
this, newValue);
1987 pendingTableSizeUpdates[1] = newValue;
1988 qCDebug(qHttp2ConnectionLog,
"[%p] Pending 2nd table size update to %u, smallest is %u",
1989 this, newValue, *pendingTableSizeUpdates[0]);
1993 case Settings::INITIAL_WINDOW_SIZE_ID: {
1994 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d",
this,
1998 if (newValue > quint32(std::numeric_limits<qint32>::max())) {
1999 connectionError(FLOW_CONTROL_ERROR,
"SETTINGS invalid initial window size");
2003 const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
2004 streamInitialSendWindowSize = qint32(newValue);
2006 qCDebug(qHttp2ConnectionLog,
"[%p] Adjusting initial window size for %zu streams by %d",
2007 this, size_t(m_streams.size()), delta);
2008 for (
const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
2012 if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
2013 stream->streamError(PROTOCOL_ERROR,
"SETTINGS window overflow"_L1);
2016 stream->m_sendWindow = sum;
2017 if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
2018 QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
2019 Qt::QueuedConnection);
2024 case Settings::MAX_CONCURRENT_STREAMS_ID: {
2025 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d",
this,
2027 m_peerMaxConcurrentStreams = newValue;
2030 case Settings::MAX_FRAME_SIZE_ID: {
2031 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_FRAME_SIZE %d",
this, newValue);
2032 if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
2033 connectionError(PROTOCOL_ERROR,
"SETTINGS max frame size is out of range");
2036 maxFrameSize = newValue;
2039 case Settings::MAX_HEADER_LIST_SIZE_ID: {
2040 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d",
this,
2045 m_maxHeaderListSize = newValue;
2048 case Http2::Settings::ENABLE_PUSH_ID:
2049 qCDebug(qHttp2ConnectionLog,
"[%p] Received SETTINGS ENABLE_PUSH %d",
this, newValue);
2050 if (newValue != 0 && newValue != 1) {
2051 connectionError(PROTOCOL_ERROR,
"SETTINGS peer sent illegal value for ENABLE_PUSH");
2054 if (m_connectionType == Type::Client) {
2055 if (newValue == 1) {
2056 connectionError(PROTOCOL_ERROR,
"SETTINGS server sent ENABLE_PUSH=1");
2060 pushPromiseEnabled = newValue;
2070#include "moc_qhttp2connection_p.cpp"
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")