Remove stream state from OgHttp2Session once a stream is closed.

This CL creates OgHttp2Session::CloseStream(), which calls the visitor's
OnCloseStream() and removes the stream state from OgHttp2Session's internal
map. This completes a TODO in its own right and also prepares for the change to
check the size of the internal map against the peer's advertised
SETTINGS_MAX_CONCURRENT_STREAMS value when deciding whether to create a stream
immediately or queue it for later.

PiperOrigin-RevId: 398512884
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index d7ce5e0..0ec920e 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -5,6 +5,7 @@
 
 #include "absl/memory/memory.h"
 #include "absl/strings/escaping.h"
+#include "http2/adapter/http2_protocol.h"
 #include "http2/adapter/oghttp2_util.h"
 #include "spdy/core/spdy_protocol.h"
 
@@ -342,7 +343,7 @@
         // If this endpoint is resetting the stream, the stream should be
         // closed. This endpoint is already aware of the outbound RST_STREAM and
         // its error code, so close with NO_ERROR.
-        visitor_.OnCloseStream(c.stream_id(), Http2ErrorCode::NO_ERROR);
+        CloseStream(c.stream_id(), Http2ErrorCode::NO_ERROR);
       }
 
       frames_.pop_front();
@@ -397,7 +398,7 @@
       break;
     } else if (length == DataFrameSource::kError) {
       source_can_produce = false;
-      visitor_.OnCloseStream(stream_id, Http2ErrorCode::INTERNAL_ERROR);
+      CloseStream(stream_id, Http2ErrorCode::INTERNAL_ERROR);
       break;
     }
     const bool fin = end_data ? state.outbound_body->send_fin() : false;
@@ -501,10 +502,8 @@
   const bool end_stream = data_source == nullptr;
   if (end_stream) {
     if (iter->second.half_closed_remote) {
-      visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR);
+      CloseStream(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 = std::move(data_source);
@@ -607,7 +606,7 @@
       options_.perspective == Perspective::kClient) {
     // From the client's perspective, the stream can be closed if it's already
     // half_closed_local.
-    visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR);
+    CloseStream(stream_id, Http2ErrorCode::NO_ERROR);
   }
 }
 
@@ -642,9 +641,7 @@
     write_scheduler_.UnregisterStream(stream_id);
   }
   visitor_.OnRstStream(stream_id, TranslateErrorCode(error_code));
-  // TODO(birenroy): Consider bundling "close stream" behavior into a dedicated
-  // method that also cleans up the stream map.
-  visitor_.OnCloseStream(stream_id, TranslateErrorCode(error_code));
+  CloseStream(stream_id, TranslateErrorCode(error_code));
 }
 
 void OgHttp2Session::OnSettings() {
@@ -841,7 +838,7 @@
   state.half_closed_local = true;
   if (options_.perspective == Perspective::kServer) {
     if (state.half_closed_remote) {
-      visitor_.OnCloseStream(stream_id, Http2ErrorCode::NO_ERROR);
+      CloseStream(stream_id, Http2ErrorCode::NO_ERROR);
     } else {
       // Since the peer has not yet ended the stream, this endpoint should
       // send a RST_STREAM NO_ERROR. See RFC 7540 Section 8.1.
@@ -849,8 +846,6 @@
           stream_id, spdy::SpdyErrorCode::ERROR_CODE_NO_ERROR));
       // Enqueuing the RST_STREAM also invokes OnCloseStream.
     }
-    // TODO(birenroy): the server adapter should probably delete stream state
-    // when calling visitor_.OnCloseStream.
   }
 }
 
@@ -897,5 +892,11 @@
   SendHeaders(stream_id, std::move(headers), end_stream);
 }
 
+void OgHttp2Session::CloseStream(Http2StreamId stream_id,
+                                 Http2ErrorCode error_code) {
+  visitor_.OnCloseStream(stream_id, error_code);
+  stream_map_.erase(stream_id);
+}
+
 }  // namespace adapter
 }  // namespace http2
diff --git a/http2/adapter/oghttp2_session.h b/http2/adapter/oghttp2_session.h
index d5973a5..cd9bbda 100644
--- a/http2/adapter/oghttp2_session.h
+++ b/http2/adapter/oghttp2_session.h
@@ -243,6 +243,9 @@
                     std::unique_ptr<DataFrameSource> data_source,
                     void* user_data);
 
+  // Closes the given `stream_id` with the given `error_code`.
+  void CloseStream(Http2StreamId stream_id, Http2ErrorCode error_code);
+
   // Receives events when inbound frames are parsed.
   Http2VisitorInterface& visitor_;
 
diff --git a/http2/adapter/oghttp2_session_test.cc b/http2/adapter/oghttp2_session_test.cc
index 8e088ee..4fdee88 100644
--- a/http2/adapter/oghttp2_session_test.cc
+++ b/http2/adapter/oghttp2_session_test.cc
@@ -588,11 +588,10 @@
 
   EXPECT_GT(session.GetHpackDecoderDynamicTableSize(), 0);
 
-  // TODO(birenroy): drop stream state when streams are closed. It should no
-  // longer be possible to set user data.
+  // It should no longer be possible to set user data on a closed stream.
   const char* kSentinel3 = "another arbitrary pointer";
   session.SetStreamUserData(3, const_cast<char*>(kSentinel3));
-  EXPECT_EQ(kSentinel3, session.GetStreamUserData(3));
+  EXPECT_EQ(nullptr, session.GetStreamUserData(3));
 
   EXPECT_EQ(session.GetRemoteWindowSize(),
             kInitialFlowControlWindowSize + 1000);