Adds a method to the Http2Adapter API to retrieve the send window size for a given stream.
PiperOrigin-RevId: 378868483
diff --git a/http2/adapter/http2_adapter.h b/http2/adapter/http2_adapter.h
index 51edda3..4f2b3f3 100644
--- a/http2/adapter/http2_adapter.h
+++ b/http2/adapter/http2_adapter.h
@@ -72,6 +72,9 @@
// Returns the connection-level flow control window advertised by the peer.
virtual int GetSendWindowSize() const = 0;
+ // Returns the stream-level flow control window advertised by the peer.
+ virtual int GetStreamSendWindowSize(Http2StreamId stream_id) const = 0;
+
// Returns the current upper bound on the flow control receive window for this
// stream. This value does not account for data received from the peer.
virtual int GetStreamReceiveWindowLimit(Http2StreamId stream_id) const = 0;
diff --git a/http2/adapter/nghttp2_adapter.cc b/http2/adapter/nghttp2_adapter.cc
index e932838..43d81cf 100644
--- a/http2/adapter/nghttp2_adapter.cc
+++ b/http2/adapter/nghttp2_adapter.cc
@@ -106,6 +106,11 @@
return session_->GetRemoteWindowSize();
}
+int NgHttp2Adapter::GetStreamSendWindowSize(Http2StreamId stream_id) const {
+ return nghttp2_session_get_stream_remote_window_size(session_->raw_ptr(),
+ stream_id);
+}
+
int NgHttp2Adapter::GetStreamReceiveWindowLimit(Http2StreamId stream_id) const {
return nghttp2_session_get_stream_effective_local_window_size(
session_->raw_ptr(), stream_id);
diff --git a/http2/adapter/nghttp2_adapter.h b/http2/adapter/nghttp2_adapter.h
index 26b41e6..6f8f703 100644
--- a/http2/adapter/nghttp2_adapter.h
+++ b/http2/adapter/nghttp2_adapter.h
@@ -50,6 +50,8 @@
void Send() override;
int GetSendWindowSize() const override;
+ int GetStreamSendWindowSize(Http2StreamId stream_id) const override;
+
int GetStreamReceiveWindowLimit(Http2StreamId stream_id) const override;
int GetStreamReceiveWindowSize(Http2StreamId stream_id) const override;
int GetReceiveWindowSize() const override;
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index 59ec4ea..0ff57af 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -290,10 +290,41 @@
EXPECT_EQ(kInitialFlowControlWindowSize,
adapter->GetStreamReceiveWindowLimit(stream_id));
+ // Some data was sent, so the remaining send window size should be less than
+ // the default.
+ EXPECT_LT(adapter->GetStreamSendWindowSize(stream_id),
+ kInitialFlowControlWindowSize);
+ EXPECT_GT(adapter->GetStreamSendWindowSize(stream_id), 0);
+ // Send window for a nonexistent stream is not available.
+ EXPECT_EQ(-1, adapter->GetStreamSendWindowSize(stream_id + 2));
+
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
spdy::SpdyFrameType::DATA}));
EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
+ visitor.Clear();
EXPECT_FALSE(adapter->session().want_write());
+
+ stream_id =
+ adapter->SubmitRequest(ToHeaders({{":method", "POST"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"}}),
+ nullptr, nullptr);
+ EXPECT_GT(stream_id, 0);
+ EXPECT_TRUE(adapter->session().want_write());
+ const char* kSentinel2 = "arbitrary pointer 2";
+ EXPECT_EQ(nullptr, adapter->GetStreamUserData(stream_id));
+ adapter->SetStreamUserData(stream_id, const_cast<char*>(kSentinel2));
+
+ adapter->Send();
+ EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+
+ EXPECT_EQ(kSentinel2, adapter->GetStreamUserData(stream_id));
+
+ // No data was sent (just HEADERS), so the remaining send window size should
+ // still be the default.
+ EXPECT_EQ(adapter->GetStreamSendWindowSize(stream_id),
+ kInitialFlowControlWindowSize);
}
// This is really a test of the MakeZeroCopyDataFrameSource adapter, but I
@@ -604,7 +635,7 @@
EXPECT_FALSE(adapter->session().want_write());
const absl::string_view kBody = "This is an example response body.";
- TestDataFrameSource body1(visitor, kBody);
+ TestDataFrameSource body1(visitor, kBody, /*has_fin=*/false);
int submit_result = adapter->SubmitResponse(
1,
ToHeaders({{":status", "404"},
@@ -618,13 +649,19 @@
adapter->SetStreamUserData(1, nullptr);
EXPECT_EQ(nullptr, adapter->GetStreamUserData(1));
- EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
adapter->Send();
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS,
spdy::SpdyFrameType::DATA}));
EXPECT_THAT(visitor.data(), testing::HasSubstr(kBody));
EXPECT_FALSE(adapter->session().want_write());
+
+ // Some data was sent, so the remaining send window size should be less than
+ // the default.
+ EXPECT_LT(adapter->GetStreamSendWindowSize(1), kInitialFlowControlWindowSize);
+ EXPECT_GT(adapter->GetStreamSendWindowSize(1), 0);
+ // Send window for a nonexistent stream is not available.
+ EXPECT_EQ(adapter->GetStreamSendWindowSize(3), -1);
}
// Should also test: client attempts shutdown, server attempts shutdown after an
diff --git a/http2/adapter/oghttp2_adapter.cc b/http2/adapter/oghttp2_adapter.cc
index d507c0c..e5b89e5 100644
--- a/http2/adapter/oghttp2_adapter.cc
+++ b/http2/adapter/oghttp2_adapter.cc
@@ -87,6 +87,10 @@
return session_->GetRemoteWindowSize();
}
+int OgHttp2Adapter::GetStreamSendWindowSize(Http2StreamId stream_id) const {
+ return session_->GetStreamSendWindowSize(stream_id);
+}
+
int OgHttp2Adapter::GetStreamReceiveWindowLimit(Http2StreamId stream_id) const {
return session_->GetStreamReceiveWindowLimit(stream_id);
}
diff --git a/http2/adapter/oghttp2_adapter.h b/http2/adapter/oghttp2_adapter.h
index 7e9ee7b..c15f20e 100644
--- a/http2/adapter/oghttp2_adapter.h
+++ b/http2/adapter/oghttp2_adapter.h
@@ -36,6 +36,7 @@
void SubmitMetadata(Http2StreamId stream_id, bool fin) override;
void Send() override;
int GetSendWindowSize() const override;
+ int GetStreamSendWindowSize(Http2StreamId stream_id) const override;
int GetStreamReceiveWindowLimit(Http2StreamId stream_id) const override;
int GetStreamReceiveWindowSize(Http2StreamId stream_id) const override;
int GetReceiveWindowSize() const override;
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index 27bca42..e8b85fa 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -66,6 +66,14 @@
return true;
}
+int OgHttp2Session::GetStreamSendWindowSize(Http2StreamId stream_id) const {
+ auto it = stream_map_.find(stream_id);
+ if (it != stream_map_.end()) {
+ return it->second.send_window;
+ }
+ return -1;
+}
+
int OgHttp2Session::GetStreamReceiveWindowLimit(Http2StreamId stream_id) const {
auto it = stream_map_.find(stream_id);
if (it != stream_map_.end()) {
@@ -329,6 +337,8 @@
if (iter->second.half_closed_remote) {
visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR);
}
+ // TODO(birenroy): the server adapter should probably delete stream state
+ // when calling visitor_.OnCloseStream.
} else {
// Add data source to stream state
iter->second.outbound_body = data_source;
@@ -587,6 +597,8 @@
stream_id, spdy::SpdyErrorCode::ERROR_CODE_NO_ERROR));
}
visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR);
+ // TODO(birenroy): the server adapter should probably delete stream state
+ // when calling visitor_.OnCloseStream.
}
}
diff --git a/http2/adapter/oghttp2_session.h b/http2/adapter/oghttp2_session.h
index f325f10..68dca30 100644
--- a/http2/adapter/oghttp2_session.h
+++ b/http2/adapter/oghttp2_session.h
@@ -57,6 +57,9 @@
// Resumes a stream that was previously blocked. Returns true on success.
bool ResumeStream(Http2StreamId stream_id);
+ // Returns the peer's outstanding stream receive window for the given stream.
+ int GetStreamSendWindowSize(Http2StreamId stream_id) const;
+
// Returns the current upper bound on the flow control receive window for this
// stream.
int GetStreamReceiveWindowLimit(Http2StreamId stream_id) const;
diff --git a/http2/adapter/oghttp2_session_test.cc b/http2/adapter/oghttp2_session_test.cc
index 40f279d..ae18323 100644
--- a/http2/adapter/oghttp2_session_test.cc
+++ b/http2/adapter/oghttp2_session_test.cc
@@ -230,6 +230,14 @@
visitor.Clear();
EXPECT_FALSE(session.want_write());
+ // Some data was sent, so the remaining send window size should be less than
+ // the default.
+ EXPECT_LT(session.GetStreamSendWindowSize(stream_id),
+ kInitialFlowControlWindowSize);
+ EXPECT_GT(session.GetStreamSendWindowSize(stream_id), 0);
+ // Send window for a nonexistent stream is not available.
+ EXPECT_EQ(-1, session.GetStreamSendWindowSize(stream_id + 2));
+
stream_id =
session.SubmitRequest(ToHeaders({{":method", "POST"},
{":scheme", "http"},
@@ -245,6 +253,11 @@
session.Send();
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::HEADERS}));
+
+ // No data was sent (just HEADERS), so the remaining send window size should
+ // still be the default.
+ EXPECT_EQ(session.GetStreamSendWindowSize(stream_id),
+ kInitialFlowControlWindowSize);
}
// This test exercises the case where the client request body source is read
@@ -536,7 +549,8 @@
visitor.Clear();
EXPECT_FALSE(session.want_write());
- TestDataFrameSource body1(visitor, "This is an example response body.");
+ TestDataFrameSource body1(visitor, "This is an example response body.",
+ /*has_fin=*/false);
int submit_result = session.SubmitResponse(
1,
ToHeaders({{":status", "404"},
@@ -550,11 +564,16 @@
session.SetStreamUserData(1, nullptr);
EXPECT_EQ(nullptr, session.GetStreamUserData(1));
- EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::NO_ERROR));
session.Send();
EXPECT_THAT(visitor.data(),
EqualsFrames({SpdyFrameType::HEADERS, SpdyFrameType::DATA}));
EXPECT_FALSE(session.want_write());
+
+ // Some data was sent, so the remaining send window size should be less than
+ // the default.
+ EXPECT_LT(session.GetStreamSendWindowSize(1), kInitialFlowControlWindowSize);
+ // Send window for a nonexistent stream is not available.
+ EXPECT_EQ(session.GetStreamSendWindowSize(3), -1);
}
TEST(OgHttp2SessionTest, ServerStartShutdown) {