97 Q_DISABLE_COPY_MOVE(QHttp2Stream)
100 enum class State { Idle, ReservedRemote, Open, HalfClosedLocal, HalfClosedRemote, Closed };
102 constexpr static quint8 DefaultPriority = 127;
104 ~QHttp2Stream()
noexcept;
107 quint32 streamID()
const noexcept {
return m_streamID; }
110 bool isUploadBlocked()
const noexcept;
111 bool isUploadingDATA()
const noexcept {
return m_uploadByteDevice !=
nullptr; }
112 State state()
const noexcept {
return m_state; }
113 bool isActive()
const noexcept {
return m_state != State::Closed && m_state != State::Idle; }
114 bool isPromisedStream()
const noexcept {
return m_isReserved; }
115 bool wasReset()
const noexcept {
return m_RST_STREAM_received.has_value() ||
116 m_RST_STREAM_sent.has_value(); }
117 bool wasResetbyPeer()
const noexcept {
return m_RST_STREAM_received.has_value(); }
118 quint32 RST_STREAMCodeReceived()
const noexcept {
return m_RST_STREAM_received.value_or(0); }
119 quint32 RST_STREAMCodeSent()
const noexcept {
return m_RST_STREAM_sent.value_or(0); }
121 HPack::HttpHeader receivedHeaders()
const noexcept {
return m_headers; }
123 QByteDataBuffer downloadBuffer()
const noexcept {
return m_downloadBuffer; }
124 QByteDataBuffer takeDownloadBuffer()
noexcept {
return std::exchange(m_downloadBuffer, {}); }
125 void clearDownloadBuffer() { m_downloadBuffer.clear(); }
128 void headersReceived(
const HPack::HttpHeader &headers,
bool endStream);
129 void headersUpdated();
130 void errorOccurred(Http2::Http2Error errorCode,
const QString &errorString);
131 void stateChanged(QHttp2Stream::State newState);
132 void promisedStreamReceived(quint32 newStreamID);
133 void uploadBlocked();
134 void dataReceived(
const QByteArray &data,
bool endStream);
135 void rstFrameReceived(quint32 errorCode);
137 void bytesWritten(qint64 bytesWritten);
138 void uploadDeviceError(
const QString &errorString);
139 void uploadFinished();
142 bool sendRST_STREAM(Http2::Http2Error errorCode);
143 bool sendHEADERS(
const HPack::HttpHeader &headers,
bool endStream,
144 quint8 priority = DefaultPriority);
145 bool sendDATA(
const QByteArray &payload,
bool endStream);
146 bool sendDATA(QIODevice *device,
bool endStream);
147 bool sendDATA(QNonContiguousByteDevice *device,
bool endStream);
148 void sendWINDOW_UPDATE(quint32 delta);
151 void maybeResumeUpload();
152 void uploadDeviceReadChannelFinished();
153 void uploadDeviceDestroyed();
156 friend class QHttp2Connection;
157 QHttp2Stream(QHttp2Connection *connection, quint32 streamID)
noexcept;
159 [[nodiscard]] QHttp2Connection *getConnection()
const
161 return qobject_cast<QHttp2Connection *>(parent());
164 enum class StateTransition {
171 void setState(State newState);
172 void transitionState(StateTransition transition);
173 void internalSendDATA();
174 void finishSendDATA();
176 void handleDATA(
const Http2::Frame &inboundFrame);
177 void handleHEADERS(Http2::FrameFlags frameFlags,
const HPack::HttpHeader &headers);
178 void handleRST_STREAM(
const Http2::Frame &inboundFrame);
179 void handleWINDOW_UPDATE(
const Http2::Frame &inboundFrame);
181 void finishWithError(Http2::Http2Error errorCode,
const QString &message);
182 void finishWithError(Http2::Http2Error errorCode);
184 void streamError(Http2::Http2Error errorCode,
185 QLatin1StringView message);
188 const quint32 m_streamID = 0;
189 qint32 m_recvWindow = 0;
190 qint32 m_sendWindow = 0;
191 bool m_endStreamAfterDATA =
false;
192 std::optional<quint32> m_RST_STREAM_received;
193 std::optional<quint32> m_RST_STREAM_sent;
195 QIODevice *m_uploadDevice =
nullptr;
196 QNonContiguousByteDevice *m_uploadByteDevice =
nullptr;
198 QByteDataBuffer m_downloadBuffer;
199 State m_state = State::Idle;
200 HPack::HttpHeader m_headers;
201 bool m_isReserved =
false;
202 bool m_owningByteDevice =
false;
204 friend tst_QHttp2Connection;
210 Q_DISABLE_COPY_MOVE(QHttp2Connection)
213 enum class CreateStreamError {
214 MaxConcurrentStreamsReached,
219 Q_ENUM(CreateStreamError)
221 enum class PingState {
223 PongSignatureIdentical,
224 PongSignatureChanged,
229 [[nodiscard]]
static QHttp2Connection *
230 createUpgradedConnection(QIODevice *socket,
const QHttp2Configuration &config);
232 [[nodiscard]]
static QHttp2Connection *createDirectConnection(QIODevice *socket,
233 const QHttp2Configuration &config);
234 [[nodiscard]]
static QHttp2Connection *
235 createDirectServerConnection(QIODevice *socket,
const QHttp2Configuration &config);
238 [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream();
240 QHttp2Stream *getStream(quint32 streamId)
const;
241 QHttp2Stream *promisedStream(
const QUrl &streamKey)
const
243 if (quint32 id = m_promisedStreams.value(streamKey, 0); id)
244 return m_streams.value(id);
248 void close(Http2::Http2Error error = Http2::HTTP2_NO_ERROR) { sendGOAWAY(error); }
250 bool isGoingAway()
const noexcept {
return m_goingAway; }
252 quint32 maxConcurrentStreams()
const noexcept {
return m_maxConcurrentStreams; }
253 quint32 peerMaxConcurrentStreams()
const noexcept {
return m_peerMaxConcurrentStreams; }
255 quint32 maxHeaderListSize()
const noexcept {
return m_maxHeaderListSize; }
257 bool isUpgradedConnection()
const noexcept {
return m_upgradedConnection; }
260 void newIncomingStream(QHttp2Stream *stream);
261 void newPromisedStream(QHttp2Stream *stream);
262 void errorReceived();
263 void connectionClosed();
264 void settingsFrameReceived();
265 void pingFrameReceived(QHttp2Connection::PingState state);
266 void errorOccurred(Http2::Http2Error errorCode,
const QString &errorString);
267 void receivedGOAWAY(Http2::Http2Error errorCode, quint32 lastStreamID);
268 void receivedEND_STREAM(quint32 streamID);
269 void incomingStreamErrorOccured(CreateStreamError error);
273 bool sendPing(QByteArrayView data);
274 void handleReadyRead();
275 void handleConnectionClosure();
278 friend class QHttp2Stream;
279 [[nodiscard]] QIODevice *getSocket()
const {
return qobject_cast<QIODevice *>(parent()); }
281 QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> createLocalStreamInternal();
282 QHttp2Stream *createStreamInternal_impl(quint32 streamID);
284 bool isInvalidStream(quint32 streamID)
noexcept;
285 bool streamWasResetLocally(quint32 streamID)
noexcept;
287 void connectionError(Http2::Http2Error errorCode,
288 const char *message);
289 void setH2Configuration(QHttp2Configuration config);
291 void registerStreamAsResetLocally(quint32 streamID);
292 qsizetype numActiveStreamsImpl(quint32 mask)
const noexcept;
293 qsizetype numActiveRemoteStreams()
const noexcept;
294 qsizetype numActiveLocalStreams()
const noexcept;
296 bool sendClientPreface();
298 bool sendServerPreface();
299 bool serverCheckClientPreface();
300 bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
301 bool sendGOAWAY(Http2::Http2Error errorCode);
302 bool sendSETTINGS_ACK();
305 void handleHEADERS();
306 void handlePRIORITY();
307 void handleRST_STREAM();
308 void handleSETTINGS();
309 void handlePUSH_PROMISE();
312 void handleWINDOW_UPDATE();
313 void handleCONTINUATION();
315 void handleContinuedHEADERS();
317 bool acceptSetting(Http2::Settings identifier, quint32 newValue);
319 bool readClientPreface();
321 explicit QHttp2Connection(QIODevice *socket);
323 enum class Type { Client, Server } m_connectionType = Type::Client;
325 bool waitingForSettingsACK =
false;
327 static constexpr quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
330 HPack::Decoder decoder = HPack::Decoder(HPack::FieldLookupTable::DefaultSize);
331 HPack::Encoder encoder = HPack::Encoder(HPack::FieldLookupTable::DefaultSize,
true);
338 std::array<std::optional<quint32>, 2> pendingTableSizeUpdates;
340 QHttp2Configuration m_config;
341 QHash<quint32, QPointer<QHttp2Stream>> m_streams;
342 QSet<quint32> m_blockedStreams;
343 QHash<QUrl, quint32> m_promisedStreams;
344 QList<quint32> m_resetStreamIDs;
346 std::optional<QByteArray> m_lastPingSignature = std::nullopt;
347 quint32 m_nextStreamID = 1;
351 quint32 maxFrameSize = Http2::minPayloadLimit;
353 Http2::FrameReader frameReader;
354 Http2::Frame inboundFrame;
355 Http2::FrameWriter frameWriter;
359 bool continuationExpected =
false;
360 std::vector<Http2::Frame> continuedFrames;
366 quint32 m_peerMaxConcurrentStreams = Http2::maxConcurrentStreams;
373 quint32 m_maxConcurrentStreams = Http2::maxConcurrentStreams;
378 qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize;
383 qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize;
386 qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize;
390 qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
391 qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
395 quint32 m_maxHeaderListSize = (std::numeric_limits<quint32>::max)();
399 bool m_upgradedConnection =
false;
400 bool m_goingAway =
false;
401 bool pushPromiseEnabled =
false;
402 quint32 m_lastIncomingStreamID = Http2::connectionStreamID;
404 bool m_prefaceSent =
false;
407 bool m_waitingForClientPreface =
false;
409 friend tst_QHttp2Connection;