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;
109 ~QHttp2Stream()
noexcept;
112 quint32 streamID()
const noexcept {
return m_streamID; }
115 bool isUploadBlocked()
const noexcept;
116 bool isUploadingDATA()
const noexcept {
return m_uploadByteDevice !=
nullptr; }
117 State state()
const noexcept {
return m_state; }
118 bool isActive()
const noexcept {
return m_state != State::Closed && m_state != State::Idle; }
119 bool isPromisedStream()
const noexcept {
return m_isReserved; }
120 bool wasReset()
const noexcept {
return m_RST_STREAM_received.has_value() ||
121 m_RST_STREAM_sent.has_value(); }
122 bool wasResetbyPeer()
const noexcept {
return m_RST_STREAM_received.has_value(); }
123 quint32 RST_STREAMCodeReceived()
const noexcept {
return m_RST_STREAM_received.value_or(0); }
124 quint32 RST_STREAMCodeSent()
const noexcept {
return m_RST_STREAM_sent.value_or(0); }
126 HPack::HttpHeader receivedHeaders()
const noexcept {
return m_headers; }
128 QByteDataBuffer downloadBuffer()
const noexcept {
return m_downloadBuffer; }
129 QByteDataBuffer takeDownloadBuffer()
noexcept {
return std::exchange(m_downloadBuffer, {}); }
130 void clearDownloadBuffer() { m_downloadBuffer.clear(); }
132 Configuration configuration()
const {
return m_configuration; }
135 void headersReceived(
const HPack::HttpHeader &headers,
bool endStream);
136 void headersUpdated();
137 void errorOccurred(Http2::Http2Error errorCode,
const QString &errorString);
138 void stateChanged(QHttp2Stream::State newState);
139 void promisedStreamReceived(quint32 newStreamID);
140 void uploadBlocked();
141 void dataReceived(
const QByteArray &data,
bool endStream);
142 void rstFrameReceived(quint32 errorCode);
144 void bytesWritten(qint64 bytesWritten);
145 void uploadDeviceError(
const QString &errorString);
146 void uploadFinished();
149 bool sendRST_STREAM(Http2::Http2Error errorCode);
150 bool sendHEADERS(
const HPack::HttpHeader &headers,
bool endStream,
151 quint8 priority = DefaultPriority);
152 bool sendDATA(
const QByteArray &payload,
bool endStream);
153 bool sendDATA(QIODevice *device,
bool endStream);
154 bool sendDATA(QNonContiguousByteDevice *device,
bool endStream);
155 void sendWINDOW_UPDATE(quint32 delta);
158 void maybeResumeUpload();
159 void uploadDeviceReadChannelFinished();
160 void uploadDeviceDestroyed();
163 friend class QHttp2Connection;
164 QHttp2Stream(QHttp2Connection *connection, quint32 streamID,
165 Configuration configuration)
noexcept;
167 [[nodiscard]] QHttp2Connection *getConnection()
const
169 return qobject_cast<QHttp2Connection *>(parent());
172 enum class StateTransition {
179 void setState(State newState);
180 void transitionState(StateTransition transition);
181 void internalSendDATA();
182 void finishSendDATA();
184 void handleDATA(
const Http2::Frame &inboundFrame);
185 void handleHEADERS(Http2::FrameFlags frameFlags,
const HPack::HttpHeader &headers);
186 void handleRST_STREAM(
const Http2::Frame &inboundFrame);
187 void handleWINDOW_UPDATE(
const Http2::Frame &inboundFrame);
189 void finishWithError(Http2::Http2Error errorCode,
const QString &message);
190 void finishWithError(Http2::Http2Error errorCode);
192 void streamError(Http2::Http2Error errorCode,
193 QLatin1StringView 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 quint32 maxConcurrentStreams()
const noexcept {
return m_maxConcurrentStreams; }
268 quint32 peerMaxConcurrentStreams()
const noexcept {
return m_peerMaxConcurrentStreams; }
270 quint32 maxHeaderListSize()
const noexcept {
return m_maxHeaderListSize; }
272 bool isUpgradedConnection()
const noexcept {
return m_upgradedConnection; }
275 void newIncomingStream(QHttp2Stream *stream);
276 void newPromisedStream(QHttp2Stream *stream);
277 void errorReceived();
278 void connectionClosed();
279 void settingsFrameReceived();
280 void pingFrameReceived(QHttp2Connection::PingState state);
281 void errorOccurred(Http2::Http2Error errorCode,
const QString &errorString);
282 void receivedGOAWAY(Http2::Http2Error errorCode, quint32 lastStreamID);
283 void receivedEND_STREAM(quint32 streamID);
284 void incomingStreamErrorOccured(CreateStreamError error);
288 bool sendPing(QByteArrayView data);
289 void handleReadyRead();
290 void handleConnectionClosure();
293 friend class QHttp2Stream;
294 [[nodiscard]] QIODevice *getSocket()
const {
return qobject_cast<QIODevice *>(parent()); }
296 QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
297 createLocalStreamInternal(QHttp2Stream::Configuration = {});
298 QHttp2Stream *createStreamInternal_impl(quint32 streamID, QHttp2Stream::Configuration = {});
300 bool isInvalidStream(quint32 streamID)
noexcept;
301 bool streamWasResetLocally(quint32 streamID)
noexcept;
303 bool streamIsIgnored(quint32 streamID)
const noexcept;
305 void connectionError(Http2::Http2Error errorCode,
const char *message,
bool logAsError =
true);
306 void setH2Configuration(QHttp2Configuration config);
308 void registerStreamAsResetLocally(quint32 streamID);
309 qsizetype numActiveStreamsImpl(quint32 mask)
const noexcept;
310 qsizetype numActiveRemoteStreams()
const noexcept;
311 qsizetype numActiveLocalStreams()
const noexcept;
313 bool sendClientPreface();
315 bool sendServerPreface();
316 bool serverCheckClientPreface();
317 bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
318 void sendClientGracefulShutdownGoaway();
319 void sendInitialServerGracefulShutdownGoaway();
320 void sendFinalServerGracefulShutdownGoaway();
321 bool sendGOAWAYFrame(Http2::Http2Error errorCode, quint32 lastSreamID);
322 void maybeCloseOnGoingAway();
323 bool sendSETTINGS_ACK();
326 void handleHEADERS();
327 void handlePRIORITY();
328 void handleRST_STREAM();
329 void handleSETTINGS();
330 void handlePUSH_PROMISE();
333 void handleWINDOW_UPDATE();
334 void handleCONTINUATION();
336 void handleContinuedHEADERS();
338 bool acceptSetting(Http2::Settings identifier, quint32 newValue);
340 bool readClientPreface();
342 explicit QHttp2Connection(QIODevice *socket);
344 enum class Type { Client, Server } m_connectionType = Type::Client;
346 bool waitingForSettingsACK =
false;
348 static constexpr quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
351 HPack::Decoder decoder = HPack::Decoder(HPack::FieldLookupTable::DefaultSize);
352 HPack::Encoder encoder = HPack::Encoder(HPack::FieldLookupTable::DefaultSize,
true);
359 std::array<std::optional<quint32>, 2> pendingTableSizeUpdates;
361 QHttp2Configuration m_config;
362 QHash<quint32, QPointer<QHttp2Stream>> m_streams;
363 QSet<quint32> m_blockedStreams;
364 QHash<QUrl, quint32> m_promisedStreams;
365 QList<quint32> m_resetStreamIDs;
367 std::optional<QByteArray> m_lastPingSignature = std::nullopt;
368 quint32 m_nextStreamID = 1;
372 quint32 maxFrameSize = Http2::minPayloadLimit;
374 Http2::FrameReader frameReader;
375 Http2::Frame inboundFrame;
376 Http2::FrameWriter frameWriter;
380 bool continuationExpected =
false;
381 std::vector<Http2::Frame> continuedFrames;
387 quint32 m_peerMaxConcurrentStreams = Http2::maxConcurrentStreams;
394 quint32 m_maxConcurrentStreams = Http2::maxConcurrentStreams;
399 qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize;
404 qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize;
407 qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize;
411 qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
412 qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
416 quint32 m_maxHeaderListSize = (std::numeric_limits<quint32>::max)();
420 bool m_upgradedConnection =
false;
421 bool m_goingAway =
false;
422 bool pushPromiseEnabled =
false;
423 quint32 m_lastIncomingStreamID = Http2::connectionStreamID;
425 quint32 m_lastStreamToProcess = Http2::lastValidStreamID;
426 static constexpr std::chrono::duration GoawayGracePeriod = std::chrono::seconds(60);
427 QDeadlineTimer m_goawayGraceTimer;
429 std::optional<quint32> m_lastGoAwayLastStreamID;
430 bool m_connectionAborted =
false;
432 enum class GracefulShutdownState {
435 AwaitingShutdownPing,
438 GracefulShutdownState m_gracefulShutdownState = GracefulShutdownState::None;
440 bool m_prefaceSent =
false;
443 bool m_waitingForClientPreface =
false;
445 friend tst_QHttp2Connection;