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,
const QString &message);
196 const quint32 m_streamID = 0;
197 qint32 m_recvWindow = 0;
198 qint32 m_sendWindow = 0;
199 bool m_endStreamAfterDATA =
false;
200 std::optional<quint32> m_RST_STREAM_received;
201 std::optional<quint32> m_RST_STREAM_sent;
203 QIODevice *m_uploadDevice =
nullptr;
204 QNonContiguousByteDevice *m_uploadByteDevice =
nullptr;
206 QByteDataBuffer m_downloadBuffer;
207 State m_state = State::Idle;
208 HPack::HttpHeader m_headers;
209 bool m_isReserved =
false;
210 bool m_owningByteDevice =
false;
212 const Configuration m_configuration;
214 friend tst_QHttp2Connection;
220 Q_DISABLE_COPY_MOVE(QHttp2Connection)
223 enum class CreateStreamError {
224 MaxConcurrentStreamsReached,
229 Q_ENUM(CreateStreamError)
231 enum class PingState {
233 PongSignatureIdentical,
234 PongSignatureChanged,
239 [[nodiscard]]
static QHttp2Connection *
240 createUpgradedConnection(QIODevice *socket,
const QHttp2Configuration &config);
242 [[nodiscard]]
static QHttp2Connection *createDirectConnection(QIODevice *socket,
243 const QHttp2Configuration &config);
244 [[nodiscard]]
static QHttp2Connection *
245 createDirectServerConnection(QIODevice *socket,
const QHttp2Configuration &config);
248 [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream()
250 return createStream(QHttp2Stream::Configuration{});
252 [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError>
253 createStream(QHttp2Stream::Configuration config);
255 QHttp2Stream *getStream(quint32 streamId)
const;
256 QHttp2Stream *promisedStream(
const QUrl &streamKey)
const
258 if (quint32 id = m_promisedStreams.value(streamKey, 0); id)
259 return m_streams.value(id);
263 void close(Http2::Http2Error errorCode = Http2::HTTP2_NO_ERROR);
265 bool isGoingAway()
const noexcept {
return m_goingAway; }
267 std::optional<quint32> lastGoAwayStreamID()
const noexcept {
return m_lastGoAwayLastStreamID; }
269 quint32 maxConcurrentStreams()
const noexcept {
return m_maxConcurrentStreams; }
270 quint32 peerMaxConcurrentStreams()
const noexcept {
return m_peerMaxConcurrentStreams; }
272 quint32 maxHeaderListSize()
const noexcept {
return m_maxHeaderListSize; }
274 bool isUpgradedConnection()
const noexcept {
return m_upgradedConnection; }
276 bool setSessionReceiveWindowSize(qint32 size);
277 quint64 totalBytesReceivedDATA()
const noexcept {
return m_totalBytesReceivedDATA; }
280 void newIncomingStream(QHttp2Stream *stream);
281 void newPromisedStream(QHttp2Stream *stream);
282 void errorReceived();
283 void connectionClosed();
284 void settingsFrameReceived();
285 void pingFrameReceived(QHttp2Connection::PingState state);
286 void errorOccurred(Http2::Http2Error errorCode,
const QString &errorString);
287 void receivedGOAWAY(Http2::Http2Error errorCode, quint32 lastStreamID);
288 void receivedEND_STREAM(quint32 streamID);
289 void incomingStreamErrorOccured(CreateStreamError error);
293 bool sendPing(QByteArrayView data);
294 void handleReadyRead();
295 void handleConnectionClosure();
298 friend class QHttp2Stream;
299 [[nodiscard]] QIODevice *getSocket()
const {
return qobject_cast<QIODevice *>(parent()); }
301 QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
302 createLocalStreamInternal(QHttp2Stream::Configuration = {});
303 QHttp2Stream *createStreamInternal_impl(quint32 streamID, QHttp2Stream::Configuration = {});
305 bool isInvalidStream(quint32 streamID)
noexcept;
306 bool streamWasResetLocally(quint32 streamID)
noexcept;
308 bool streamIsIgnored(quint32 streamID)
const noexcept;
310 void connectionError(Http2::Http2Error errorCode,
const QString &message,
311 bool logAsError =
true);
312 void setH2Configuration(QHttp2Configuration config);
314 void registerStreamAsResetLocally(quint32 streamID);
315 qsizetype numActiveStreamsImpl(quint32 mask)
const noexcept;
316 qsizetype numActiveRemoteStreams()
const noexcept;
317 qsizetype numActiveLocalStreams()
const noexcept;
319 bool sendClientPreface();
321 bool sendServerPreface();
322 bool serverCheckClientPreface();
323 bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
324 void sendClientGracefulShutdownGoaway();
325 void sendInitialServerGracefulShutdownGoaway();
326 void sendFinalServerGracefulShutdownGoaway();
327 bool sendGOAWAYFrame(Http2::Http2Error errorCode, quint32 lastSreamID);
328 void maybeCloseOnGoingAway();
329 bool sendSETTINGS_ACK();
332 void handleHEADERS();
333 void handlePRIORITY();
334 void handleRST_STREAM();
335 void handleSETTINGS();
336 void handlePUSH_PROMISE();
339 void handleWINDOW_UPDATE();
340 void handleCONTINUATION();
342 void handleContinuedHEADERS();
344 bool validateHeaderListSize(
const Http2::Frame &frame);
346 bool acceptSetting(Http2::Settings identifier, quint32 newValue);
348 bool readClientPreface();
350 explicit QHttp2Connection(QIODevice *socket);
352 enum class Type { Client, Server } m_connectionType = Type::Client;
354 bool waitingForSettingsACK =
false;
356 static constexpr quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
359 HPack::Decoder decoder = HPack::Decoder(HPack::FieldLookupTable::DefaultSize);
360 HPack::Encoder encoder = HPack::Encoder(HPack::FieldLookupTable::DefaultSize,
true);
367 std::array<std::optional<quint32>, 2> pendingTableSizeUpdates;
369 QHttp2Configuration m_config;
370 QHash<quint32, QPointer<QHttp2Stream>> m_streams;
371 QSet<quint32> m_blockedStreams;
372 QHash<QUrl, quint32> m_promisedStreams;
373 QList<quint32> m_resetStreamIDs;
375 std::optional<QByteArray> m_lastPingSignature = std::nullopt;
376 quint32 m_nextStreamID = 1;
380 quint32 maxFrameSize = Http2::minPayloadLimit;
382 Http2::FrameReader frameReader;
383 Http2::Frame inboundFrame;
384 Http2::FrameWriter frameWriter;
388 bool continuationExpected =
false;
389 std::vector<Http2::Frame> continuedFrames;
390 quint32 m_headerBlockSize = 0;
396 quint32 m_peerMaxConcurrentStreams = Http2::maxConcurrentStreams;
403 quint32 m_maxConcurrentStreams = Http2::maxConcurrentStreams;
408 qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize;
413 qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize;
416 qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize;
418 quint64 m_totalBytesReceivedDATA = 0;
422 qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
423 qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
427 quint32 m_maxHeaderListSize = (std::numeric_limits<quint32>::max)();
432 bool m_upgradedConnection =
false;
433 bool m_goingAway =
false;
434 bool pushPromiseEnabled =
false;
435 quint32 m_lastIncomingStreamID = Http2::connectionStreamID;
437 quint32 m_lastStreamToProcess = Http2::lastValidStreamID;
438 static constexpr std::chrono::duration GoawayGracePeriod = std::chrono::seconds(60);
439 QDeadlineTimer m_goawayGraceTimer;
441 std::optional<quint32> m_lastGoAwayLastStreamID;
442 bool m_connectionAborted =
false;
444 enum class GracefulShutdownState {
447 AwaitingShutdownPing,
450 GracefulShutdownState m_gracefulShutdownState = GracefulShutdownState::None;
452 bool m_prefaceSent =
false;
455 bool m_waitingForClientPreface =
false;
457 friend tst_QHttp2Connection;