Adds a method to Http2Adapter to fetch the highest received stream ID.

PiperOrigin-RevId: 378176441
diff --git a/http2/adapter/http2_adapter.h b/http2/adapter/http2_adapter.h
index 0dbb54a..293e2fb 100644
--- a/http2/adapter/http2_adapter.h
+++ b/http2/adapter/http2_adapter.h
@@ -72,6 +72,10 @@
   // Returns the connection-level flow control window for the peer.
   virtual int GetPeerConnectionWindow() const = 0;
 
+  // Gets the highest stream ID value seen in a frame received by this endpoint.
+  // This method is only guaranteed to work for server endpoints.
+  virtual Http2StreamId GetHighestReceivedStreamId() const = 0;
+
   // Marks the given amount of data as consumed for the given stream, which
   // enables the implementation layer to send WINDOW_UPDATEs as appropriate.
   virtual void MarkDataConsumedForStream(Http2StreamId stream_id,
diff --git a/http2/adapter/nghttp2_adapter.cc b/http2/adapter/nghttp2_adapter.cc
index 947bb1f..deb4468 100644
--- a/http2/adapter/nghttp2_adapter.cc
+++ b/http2/adapter/nghttp2_adapter.cc
@@ -106,6 +106,10 @@
   return session_->GetRemoteWindowSize();
 }
 
+Http2StreamId NgHttp2Adapter::GetHighestReceivedStreamId() const {
+  return nghttp2_session_get_last_proc_stream_id(session_->raw_ptr());
+}
+
 void NgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id,
                                                size_t num_bytes) {
   int rc = session_->Consume(stream_id, num_bytes);
diff --git a/http2/adapter/nghttp2_adapter.h b/http2/adapter/nghttp2_adapter.h
index 61b906e..d37e3c9 100644
--- a/http2/adapter/nghttp2_adapter.h
+++ b/http2/adapter/nghttp2_adapter.h
@@ -51,6 +51,8 @@
 
   int GetPeerConnectionWindow() const override;
 
+  Http2StreamId GetHighestReceivedStreamId() const override;
+
   void MarkDataConsumedForStream(Http2StreamId stream_id,
                                  size_t num_bytes) override;
 
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index 90b0166..2e2a1bf 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -41,6 +41,8 @@
               testing::StrEq(spdy::kHttp2ConnectionHeaderPrefix));
   visitor.Clear();
 
+  EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+
   const std::string initial_frames = TestFrameSequence()
                                          .ServerPreface()
                                          .Ping(42)
@@ -114,6 +116,8 @@
                                             spdy::SpdyFrameType::HEADERS}));
   visitor.Clear();
 
+  EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+
   const std::string stream_frames =
       TestFrameSequence()
           .Headers(1,
@@ -145,6 +149,9 @@
   const ssize_t stream_result = adapter->ProcessBytes(stream_frames);
   EXPECT_EQ(stream_frames.size(), stream_result);
 
+  // Should be 3, but this method only works for server adapters.
+  EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+
   // Even though the client recieved a GOAWAY, streams 1 and 5 are still active.
   EXPECT_TRUE(adapter->session().want_read());
 
@@ -159,6 +166,10 @@
                             .Data(1, "", true)
                             .RstStream(5, Http2ErrorCode::REFUSED_STREAM)
                             .Serialize());
+
+  // Should be 5, but this method only works for server adapters.
+  EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+
   // After receiving END_STREAM for 1 and RST_STREAM for 5, the session no
   // longer expects reads.
   EXPECT_FALSE(adapter->session().want_read());
@@ -296,6 +307,8 @@
   DataSavingVisitor visitor;
   auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
 
+  EXPECT_EQ(0, adapter->GetHighestReceivedStreamId());
+
   const std::string frames = TestFrameSequence()
                                  .ClientPreface()
                                  .Ping(42)
@@ -357,6 +370,8 @@
   const ssize_t result = adapter->ProcessBytes(frames);
   EXPECT_EQ(frames.size(), result);
 
+  EXPECT_EQ(3, adapter->GetHighestReceivedStreamId());
+
   EXPECT_EQ(adapter->GetPeerConnectionWindow(),
             kDefaultInitialStreamWindowSize + 1000);
 
@@ -402,6 +417,8 @@
   const ssize_t result = adapter->ProcessBytes(frames);
   EXPECT_EQ(frames.size(), result);
 
+  EXPECT_EQ(1, adapter->GetHighestReceivedStreamId());
+
   // Server will want to send a SETTINGS ack.
   EXPECT_TRUE(adapter->session().want_write());
   adapter->Send();
diff --git a/http2/adapter/oghttp2_adapter.cc b/http2/adapter/oghttp2_adapter.cc
index 6334e4f..a71a71e 100644
--- a/http2/adapter/oghttp2_adapter.cc
+++ b/http2/adapter/oghttp2_adapter.cc
@@ -87,6 +87,10 @@
   return session_->GetRemoteWindowSize();
 }
 
+Http2StreamId OgHttp2Adapter::GetHighestReceivedStreamId() const {
+  return session_->GetHighestReceivedStreamId();
+}
+
 void OgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id,
                                                size_t num_bytes) {
   session_->Consume(stream_id, num_bytes);
diff --git a/http2/adapter/oghttp2_adapter.h b/http2/adapter/oghttp2_adapter.h
index 26d5743..1b9d90d 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 GetPeerConnectionWindow() const override;
+  Http2StreamId GetHighestReceivedStreamId() const override;
   void MarkDataConsumedForStream(Http2StreamId stream_id,
                                  size_t num_bytes) override;
   void SubmitRst(Http2StreamId stream_id, Http2ErrorCode error_code) override;
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index 05fc467..aa135ff 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -304,6 +304,8 @@
                                     size_t length,
                                     uint8_t type,
                                     uint8_t flags) {
+  highest_received_stream_id_ = std::max(static_cast<Http2StreamId>(stream_id),
+                                         highest_received_stream_id_);
   visitor_.OnFrameHeader(stream_id, length, type, flags);
 }
 
diff --git a/http2/adapter/oghttp2_session.h b/http2/adapter/oghttp2_session.h
index 778af35..8655820 100644
--- a/http2/adapter/oghttp2_session.h
+++ b/http2/adapter/oghttp2_session.h
@@ -48,6 +48,9 @@
   bool IsServerSession() const {
     return options_.perspective == Perspective::kServer;
   }
+  Http2StreamId GetHighestReceivedStreamId() const {
+    return highest_received_stream_id_;
+  }
 
   // From Http2Session.
   ssize_t ProcessBytes(absl::string_view bytes) override;
@@ -191,6 +194,7 @@
   absl::string_view remaining_preface_;
 
   Http2StreamId next_stream_id_ = 1;
+  Http2StreamId highest_received_stream_id_ = 0;
   int peer_window_ = 65535;
   int stream_receive_window_limit_ = 65535;
   int max_frame_payload_ = 16384;
diff --git a/http2/adapter/oghttp2_session_test.cc b/http2/adapter/oghttp2_session_test.cc
index 28ddb57..c6525fa 100644
--- a/http2/adapter/oghttp2_session_test.cc
+++ b/http2/adapter/oghttp2_session_test.cc
@@ -35,6 +35,7 @@
   EXPECT_FALSE(session.want_write());
   EXPECT_EQ(session.GetRemoteWindowSize(), kDefaultInitialStreamWindowSize);
   EXPECT_FALSE(session.IsServerSession());
+  EXPECT_EQ(0, session.GetHighestReceivedStreamId());
 }
 
 TEST(OgHttp2SessionTest, ClientHandlesFrames) {
@@ -64,6 +65,7 @@
 
   EXPECT_EQ(session.GetRemoteWindowSize(),
             kDefaultInitialStreamWindowSize + 1000);
+  EXPECT_EQ(0, session.GetHighestReceivedStreamId());
 
   // Should OgHttp2Session require that streams 1 and 3 have been created?
 
@@ -96,6 +98,7 @@
   EXPECT_CALL(visitor, OnGoAway(5, Http2ErrorCode::ENHANCE_YOUR_CALM, ""));
   const ssize_t stream_result = session.ProcessBytes(stream_frames);
   EXPECT_EQ(stream_frames.size(), stream_result);
+  EXPECT_EQ(3, session.GetHighestReceivedStreamId());
 }
 
 // Verifies that a client session enqueues initial SETTINGS if Send() is called
@@ -227,6 +230,7 @@
   EXPECT_FALSE(session.want_write());
   EXPECT_EQ(session.GetRemoteWindowSize(), kDefaultInitialStreamWindowSize);
   EXPECT_TRUE(session.IsServerSession());
+  EXPECT_EQ(0, session.GetHighestReceivedStreamId());
 }
 
 TEST(OgHttp2SessionTest, ServerHandlesFrames) {
@@ -297,6 +301,7 @@
 
   EXPECT_EQ(session.GetRemoteWindowSize(),
             kDefaultInitialStreamWindowSize + 1000);
+  EXPECT_EQ(3, session.GetHighestReceivedStreamId());
 }
 
 // Verifies that a server session enqueues initial SETTINGS before whatever
@@ -361,6 +366,8 @@
   const ssize_t result = session.ProcessBytes(frames);
   EXPECT_EQ(frames.size(), result);
 
+  EXPECT_EQ(1, session.GetHighestReceivedStreamId());
+
   // Server will want to send initial SETTINGS, and a SETTINGS ack.
   EXPECT_TRUE(session.want_write());
   session.Send();