diff --git a/proxygen/lib/http/webtransport/QuicWtSession.cpp b/proxygen/lib/http/webtransport/QuicWtSession.cpp index 988dd8aed2..daf565ff24 100644 --- a/proxygen/lib/http/webtransport/QuicWtSession.cpp +++ b/proxygen/lib/http/webtransport/QuicWtSession.cpp @@ -373,7 +373,12 @@ void QuicWtSessionBase::StreamManagerCallback::eventsAvailableImpl() noexcept { if (streamData.fin) { sock->setStopSendingCallback(streamId, nullptr); } else if (isWtStreamEgressFcBlocked(sess.sm_, *wh)) { - sess.quicSocket_->notifyPendingWriteOnStream(streamId, &sess); + // The cached window may lag the peer's MAX_STREAM_DATA; refreshing it + // here saves a loop iteration through notifyPendingWriteOnStream. + grantMaxData(*sock, sess.sm_, *wh); + if (isWtStreamEgressFcBlocked(sess.sm_, *wh)) { + sess.quicSocket_->notifyPendingWriteOnStream(streamId, &sess); + } } } } diff --git a/proxygen/lib/http/webtransport/test/QuicWtSessionTest.cpp b/proxygen/lib/http/webtransport/test/QuicWtSessionTest.cpp index e606ea360c..4f748aa6cd 100644 --- a/proxygen/lib/http/webtransport/test/QuicWtSessionTest.cpp +++ b/proxygen/lib/http/webtransport/test/QuicWtSessionTest.cpp @@ -846,6 +846,27 @@ TEST_F(QuicWtSessionTest, WriteFlowControlBlocked) { EXPECT_TRUE(stream.writeEOF); } +TEST_F(QuicWtSessionTest, WriteRefreshesStaleFlowControl) { + auto handle = session_->createUniStream(); + ASSERT_TRUE(handle.hasValue()); + const auto id = handle.value()->getID(); + socketDriver_.setConnectionFlowControlWindow( + std::numeric_limits::max()); + + // Peer raised MAX_STREAM_DATA after creation; the cached window is stale. + constexpr size_t kDataLength = 65'537; + socketDriver_.setStreamFlowControlWindow(id, kDataLength); + + handle.value()->writeStreamData( + folly::IOBuf::copyBuffer(std::string(kDataLength, 'a')), true, nullptr); + eventBase_.loopOnce(); + + auto& stream = socketDriver_.streams_[id]; + EXPECT_EQ(stream.writeBuf.chainLength(), kDataLength); + EXPECT_TRUE(stream.writeEOF); + EXPECT_EQ(stream.pendingWriteCb.stream, nullptr); +} + /** * Unsure if the following is possible in production (i.e. QuicTransportBase) -- * but it tests when a ::writeChain fails without a prior ::readError or