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) {