Extends WindowManager to handle the following new cases:
* one-time WINDOW_UPDATE frame increases the window beyond the persistent limit
* update to the persistent limit causes a window to become negative
PiperOrigin-RevId: 429071623
diff --git a/http2/adapter/window_manager.cc b/http2/adapter/window_manager.cc
index 9ab661d..76bde18 100644
--- a/http2/adapter/window_manager.cc
+++ b/http2/adapter/window_manager.cc
@@ -19,12 +19,7 @@
QUICHE_VLOG(2) << "WindowManager@" << this
<< " OnWindowSizeLimitChange from old limit of " << limit_
<< " to new limit of " << new_limit;
- if (new_limit > limit_) {
- window_ += (new_limit - limit_);
- } else {
- QUICHE_BUG(H2 window decrease)
- << "Window size limit decrease not currently supported.";
- }
+ window_ += (new_limit - limit_);
limit_ = new_limit;
}
@@ -68,11 +63,6 @@
}
void WindowManager::MaybeNotifyListener() {
- if (buffered_ + window_ > limit_) {
- QUICHE_LOG(ERROR) << "Flow control violation; limit: " << limit_
- << " buffered: " << buffered_ << " window: " << window_;
- return;
- }
// For the sake of efficiency, we want to send window updates if less than
// half of the max quota is available to the peer at any point in time.
const int64_t kDesiredMinWindow = limit_ / 2;
diff --git a/http2/adapter/window_manager.h b/http2/adapter/window_manager.h
index a4fe468..f4ee23f 100644
--- a/http2/adapter/window_manager.h
+++ b/http2/adapter/window_manager.h
@@ -50,6 +50,10 @@
MarkDataFlushed(bytes);
}
+ // Increments the window size without affecting the limit. Useful if this end
+ // of a stream or connection issues a one-time WINDOW_UPDATE.
+ void IncreaseWindow(int64_t delta) { window_ += delta; }
+
private:
friend class test::WindowManagerPeer;
diff --git a/http2/adapter/window_manager_test.cc b/http2/adapter/window_manager_test.cc
index e81dc94..191ebe9 100644
--- a/http2/adapter/window_manager_test.cc
+++ b/http2/adapter/window_manager_test.cc
@@ -161,6 +161,53 @@
EXPECT_THAT(call_sequence_, testing::ElementsAre(1));
}
+TEST_F(WindowManagerTest, OnWindowSizeLimitChange) {
+ wm_.MarkDataBuffered(10000);
+ EXPECT_EQ(wm_.CurrentWindowSize(), kDefaultLimit - 10000);
+ EXPECT_EQ(wm_.WindowSizeLimit(), kDefaultLimit);
+
+ wm_.OnWindowSizeLimitChange(kDefaultLimit + 1000);
+ EXPECT_EQ(wm_.CurrentWindowSize(), kDefaultLimit - 9000);
+ EXPECT_EQ(wm_.WindowSizeLimit(), kDefaultLimit + 1000);
+
+ wm_.OnWindowSizeLimitChange(kDefaultLimit - 1000);
+ EXPECT_EQ(wm_.CurrentWindowSize(), kDefaultLimit - 11000);
+ EXPECT_EQ(wm_.WindowSizeLimit(), kDefaultLimit - 1000);
+}
+
+TEST_F(WindowManagerTest, NegativeWindowSize) {
+ wm_.MarkDataBuffered(80000);
+ // 98304 window - 80000 buffered = 18304 available
+ EXPECT_EQ(wm_.CurrentWindowSize(), 18304);
+ wm_.OnWindowSizeLimitChange(65535);
+ // limit decreases by 98304 - 65535 = 32769, window becomes -14465
+ EXPECT_EQ(wm_.CurrentWindowSize(), -14465);
+ wm_.MarkDataFlushed(70000);
+ // Still 10000 bytes buffered, so window manager grants sufficient quota to
+ // reach a window of 65535 - 10000.
+ EXPECT_EQ(wm_.CurrentWindowSize(), 55535);
+ // Desired window minus existing window: 55535 - (-14465) = 70000
+ EXPECT_THAT(call_sequence_, testing::ElementsAre(70000));
+}
+
+TEST_F(WindowManagerTest, IncreaseWindow) {
+ wm_.MarkDataBuffered(1000);
+ EXPECT_EQ(wm_.CurrentWindowSize(), kDefaultLimit - 1000);
+ EXPECT_EQ(wm_.WindowSizeLimit(), kDefaultLimit);
+
+ // Increasing the window beyond the limit is allowed.
+ wm_.IncreaseWindow(5000);
+ EXPECT_EQ(wm_.CurrentWindowSize(), kDefaultLimit + 4000);
+ EXPECT_EQ(wm_.WindowSizeLimit(), kDefaultLimit);
+
+ // 80000 bytes are buffered, then flushed.
+ wm_.MarkWindowConsumed(80000);
+ // The window manager replenishes the consumed quota up to the limit.
+ EXPECT_THAT(call_sequence_, testing::ElementsAre(75000));
+ // The window is the limit, minus buffered data, as expected.
+ EXPECT_EQ(wm_.CurrentWindowSize(), kDefaultLimit - 1000);
+}
+
} // namespace
} // namespace test
} // namespace adapter