97 Q_DISABLE_COPY_MOVE(QHttp2Stream)
100 enum class State { Idle, ReservedRemote, Open, HalfClosedLocal, HalfClosedRemote, Closed };
102 constexpr static quint8 DefaultPriority = 127;
106 bool useDownloadBuffer =
true;
107 bool useHeaderBuffer =
true;
110 ~QHttp2Stream()
noexcept;
113 quint32 streamID()
const noexcept {
return m_streamID; }
116 bool isUploadBlocked()
const noexcept;
117 bool isUploadingDATA()
const noexcept {
return m_uploadByteDevice !=
nullptr; }
118 State state()
const noexcept {
return m_state; }
119 bool isActive()
const noexcept {
return m_state != State::Closed && m_state != State::Idle; }
120 bool isPromisedStream()
const noexcept {
return m_isReserved; }
121 bool wasReset()
const noexcept {
return m_RST_STREAM_received.has_value() ||
122 m_RST_STREAM_sent.has_value(); }
123 bool wasResetbyPeer()
const noexcept {
return m_RST_STREAM_received.has_value(); }
124 quint32 RST_STREAMCodeReceived()
const noexcept {
return m_RST_STREAM_received.value_or(0); }
125 quint32 RST_STREAMCodeSent()
const noexcept {
return m_RST_STREAM_sent.value_or(0); }
127 HPack::HttpHeader receivedHeaders()
const noexcept {
return m_headers; }
129 QByteDataBuffer downloadBuffer()
const noexcept {
return m_downloadBuffer; }
130 QByteDataBuffer takeDownloadBuffer()
noexcept {
return std::exchange(m_downloadBuffer, {}); }
131 void clearDownloadBuffer() { m_downloadBuffer.clear(); }
133 Configuration configuration()
const {
return m_configuration; }
136 void headersReceived(
const HPack::HttpHeader &headers,
bool endStream);
137 void headersUpdated();
138 void errorOccurred(Http2::Http2Error errorCode,
const QString &errorString);
139 void stateChanged(QHttp2Stream::State newState);
140 void promisedStreamReceived(quint32 newStreamID);
141 void uploadBlocked();
142 void dataReceived(
const QByteArray &data,
bool endStream);
143 void rstFrameReceived(quint32 errorCode);
145 void bytesWritten(qint64 bytesWritten);
146 void uploadDeviceError(
const QString &errorString);
147 void uploadFinished();
150 bool sendRST_STREAM(Http2::Http2Error errorCode);
151 bool sendHEADERS(
const HPack::HttpHeader &headers,
bool endStream,
152 quint8 priority = DefaultPriority);
153 bool sendDATA(
const QByteArray &payload,
bool endStream);
154 bool sendDATA(QIODevice *device,
bool endStream);
155 bool sendDATA(QNonContiguousByteDevice *device,
bool endStream);
156 void sendWINDOW_UPDATE(quint32 delta);
159 void maybeResumeUpload();
160 void uploadDeviceReadChannelFinished();
161 void uploadDeviceDestroyed();
164 friend class QHttp2Connection;
165 QHttp2Stream(QHttp2Connection *connection, quint32 streamID,
166 Configuration configuration)
noexcept;
168 [[nodiscard]] QHttp2Connection *getConnection()
const
170 return qobject_cast<QHttp2Connection *>(parent());
173 enum class StateTransition {
180 void setState(State newState);
181 void transitionState(StateTransition transition);
182 void internalSendDATA();
183 void finishSendDATA();
185 void handleDATA(
const Http2::Frame &inboundFrame);
186 void handleHEADERS(Http2::FrameFlags frameFlags,
const HPack::HttpHeader &headers);
187 void handleRST_STREAM(
const Http2::Frame &inboundFrame);
188 void handleWINDOW_UPDATE(
const Http2::Frame &inboundFrame);
190 void finishWithError(Http2::Http2Error errorCode,
const QString &message);
191 void finishWithError(Http2::Http2Error errorCode);
193 void streamError(Http2::Http2Error errorCode,
194 QLatin1StringView message);
197 const quint32 m_streamID = 0;
198 qint32 m_recvWindow = 0;
199 qint32 m_sendWindow = 0;
200 bool m_endStreamAfterDATA =
false;
201 std::optional<quint32> m_RST_STREAM_received;
202 std::optional<quint32> m_RST_STREAM_sent;
204 QIODevice *m_uploadDevice =
nullptr;
205 QNonContiguousByteDevice *m_uploadByteDevice =
nullptr;
207 QByteDataBuffer m_downloadBuffer;
208 State m_state = State::Idle;
209 HPack::HttpHeader m_headers;
210 bool m_isReserved =
false;
211 bool m_owningByteDevice =
false;
213 const Configuration m_configuration;
215 friend tst_QHttp2Connection;
221 Q_DISABLE_COPY_MOVE(QHttp2Connection)
224 enum class CreateStreamError {
225 MaxConcurrentStreamsReached,
230 Q_ENUM(CreateStreamError)
232 enum class PingState {
234 PongSignatureIdentical,
235 PongSignatureChanged,
240 [[nodiscard]]
static QHttp2Connection *
241 createUpgradedConnection(QIODevice *socket,
const QHttp2Configuration &config);
243 [[nodiscard]]
static QHttp2Connection *createDirectConnection(QIODevice *socket,
244 const QHttp2Configuration &config);
245 [[nodiscard]]
static QHttp2Connection *
246 createDirectServerConnection(QIODevice *socket,
const QHttp2Configuration &config);
249 [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream()
251 return createStream(QHttp2Stream::Configuration{});
253 [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError>
254 createStream(QHttp2Stream::Configuration config);
256 QHttp2Stream *getStream(quint32 streamId)
const;
257 QHttp2Stream *promisedStream(
const QUrl &streamKey)
const
259 if (quint32 id = m_promisedStreams.value(streamKey, 0); id)
260 return m_streams.value(id);
264 void close(Http2::Http2Error errorCode = Http2::HTTP2_NO_ERROR);
266 bool isGoingAway()
const noexcept {
return m_goingAway; }
268 quint32 maxConcurrentStreams()
const noexcept {
return m_maxConcurrentStreams; }
269 quint32 peerMaxConcurrentStreams()
const noexcept {
return m_peerMaxConcurrentStreams; }
271 quint32 maxHeaderListSize()
const noexcept {
return m_maxHeaderListSize; }
273 bool isUpgradedConnection()
const noexcept {
return m_upgradedConnection; }
275 bool setSessionReceiveWindowSize(qint32 size);
276 quint64 totalBytesReceivedDATA()
const noexcept {
return m_totalBytesReceivedDATA; }
279 void newIncomingStream(QHttp2Stream *stream);
280 void newPromisedStream(QHttp2Stream *stream);
281 void errorReceived();
282 void connectionClosed();
283 void settingsFrameReceived();
284 void pingFrameReceived(QHttp2Connection::PingState state);
285 void errorOccurred(Http2::Http2Error errorCode,
const QString &errorString);
286 void receivedGOAWAY(Http2::Http2Error errorCode, quint32 lastStreamID);
287 void receivedEND_STREAM(quint32 streamID);
288 void incomingStreamErrorOccured(CreateStreamError error);
292 bool sendPing(QByteArrayView data);
293 void handleReadyRead();
294 void handleConnectionClosure();
297 friend class QHttp2Stream;
298 [[nodiscard]] QIODevice *getSocket()
const {
return qobject_cast<QIODevice *>(parent()); }
300 QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
301 createLocalStreamInternal(QHttp2Stream::Configuration = {});
302 QHttp2Stream *createStreamInternal_impl(quint32 streamID, QHttp2Stream::Configuration = {});
304 bool isInvalidStream(quint32 streamID)
noexcept;
305 bool streamWasResetLocally(quint32 streamID)
noexcept;
307 bool streamIsIgnored(quint32 streamID)
const noexcept;
309 void connectionError(Http2::Http2Error errorCode,
const char *message,
bool logAsError =
true);
310 void setH2Configuration(QHttp2Configuration config);
312 void registerStreamAsResetLocally(quint32 streamID);
313 qsizetype numActiveStreamsImpl(quint32 mask)
const noexcept;
314 qsizetype numActiveRemoteStreams()
const noexcept;
315 qsizetype numActiveLocalStreams()
const noexcept;
317 bool sendClientPreface();
319 bool sendServerPreface();
320 bool serverCheckClientPreface();
321 bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
322 void sendClientGracefulShutdownGoaway();
323 void sendInitialServerGracefulShutdownGoaway();
324 void sendFinalServerGracefulShutdownGoaway();
325 bool sendGOAWAYFrame(Http2::Http2Error errorCode, quint32 lastSreamID);
326 void maybeCloseOnGoingAway();
327 bool sendSETTINGS_ACK();
330 void handleHEADERS();
331 void handlePRIORITY();
332 void handleRST_STREAM();
333 void handleSETTINGS();
334 void handlePUSH_PROMISE();
337 void handleWINDOW_UPDATE();
338 void handleCONTINUATION();
340 void handleContinuedHEADERS();
342 bool validateHeaderListSize(
const Http2::Frame &frame);
344 bool acceptSetting(Http2::Settings identifier, quint32 newValue);
346 bool readClientPreface();
348 explicit QHttp2Connection(QIODevice *socket);
350 enum class Type { Client, Server } m_connectionType = Type::Client;
352 bool waitingForSettingsACK =
false;
354 static constexpr quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
357 HPack::Decoder decoder = HPack::Decoder(HPack::FieldLookupTable::DefaultSize);
358 HPack::Encoder encoder = HPack::Encoder(HPack::FieldLookupTable::DefaultSize,
true);
365 std::array<std::optional<quint32>, 2> pendingTableSizeUpdates;
367 QHttp2Configuration m_config;
368 QHash<quint32, QPointer<QHttp2Stream>> m_streams;
369 QSet<quint32> m_blockedStreams;
370 QHash<QUrl, quint32> m_promisedStreams;
371 QList<quint32> m_resetStreamIDs;
373 std::optional<QByteArray> m_lastPingSignature = std::nullopt;
374 quint32 m_nextStreamID = 1;
378 quint32 maxFrameSize = Http2::minPayloadLimit;
380 Http2::FrameReader frameReader;
381 Http2::Frame inboundFrame;
382 Http2::FrameWriter frameWriter;
386 bool continuationExpected =
false;
387 std::vector<Http2::Frame> continuedFrames;
388 quint32 m_headerBlockSize = 0;
394 quint32 m_peerMaxConcurrentStreams = Http2::maxConcurrentStreams;
401 quint32 m_maxConcurrentStreams = Http2::maxConcurrentStreams;
406 qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize;
411 qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize;
414 qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize;
416 quint64 m_totalBytesReceivedDATA = 0;
420 qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
421 qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
425 quint32 m_maxHeaderListSize = (std::numeric_limits<quint32>::max)();
430 bool m_upgradedConnection =
false;
431 bool m_goingAway =
false;
432 bool pushPromiseEnabled =
false;
433 quint32 m_lastIncomingStreamID = Http2::connectionStreamID;
435 quint32 m_lastStreamToProcess = Http2::lastValidStreamID;
436 static constexpr std::chrono::duration GoawayGracePeriod = std::chrono::seconds(60);
437 QDeadlineTimer m_goawayGraceTimer;
439 std::optional<quint32> m_lastGoAwayLastStreamID;
440 bool m_connectionAborted =
false;
442 enum class GracefulShutdownState {
445 AwaitingShutdownPing,
448 GracefulShutdownState m_gracefulShutdownState = GracefulShutdownState::None;
450 bool m_prefaceSent =
false;
453 bool m_waitingForClientPreface =
false;
455 friend tst_QHttp2Connection;