Move DispatchControlMessage out of the MoqtBidiStreamBase class.

PiperOrigin-RevId: 927506565
diff --git a/quiche/quic/moqt/moqt_bidi_stream.h b/quiche/quic/moqt/moqt_bidi_stream.h
index 1dd88aa..2c9def3 100644
--- a/quiche/quic/moqt/moqt_bidi_stream.h
+++ b/quiche/quic/moqt/moqt_bidi_stream.h
@@ -11,7 +11,6 @@
 #include <type_traits>
 #include <utility>
 
-#include "absl/base/casts.h"
 #include "absl/base/nullability.h"
 #include "absl/status/status.h"
 #include "absl/strings/str_cat.h"
@@ -129,24 +128,41 @@
   void OnFatalError(absl::Status status);
 
   MoqtControlStreamParser* stream_parser() { return stream_parser_.get(); }
+  const MoqtControlMessageParser& message_parser() const {
+    return message_parser_;
+  }
   MoqtFramer* framer() const { return framer_; }
   webtransport::Stream* stream() const {
     return stream_parser_ != nullptr ? stream_parser_->stream() : nullptr;
   }
 
+ private:
+  friend class test::MoqtBidiStreamTestWrapper;
+
+  MoqtFramer* absl_nonnull framer_;
+  std::unique_ptr<MoqtControlStreamParser> absl_nullable stream_parser_;
+  MoqtControlMessageParser message_parser_;
+  MoqtControlMessageQueue outgoing_message_queue_;
+  BidiStreamDeletedCallback stream_deleted_callback_;
+  SessionErrorCallback session_error_callback_;
+};
+
+// DispatchControlMessage is wrapped into a class so that the caller class can
+// provide it access to private OnControlMessage methods.
+class ControlMessageDispatcher {
+ public:
   // Parses the supplied control message. If the message is well-formed, and the
   // class defines an `OnControlMessage` method that accepts it, it is passed to
   // that method. Otherwise, an appropriate error message is returned;
   // `stream_type` is used to format that message.
-  template <typename Subclass>
-  absl::Status DispatchControlMessage(const MoqtRawControlMessage& message,
-                                      absl::string_view stream_type) {
-    static_assert(!std::is_same_v<Subclass, MoqtBidiStreamBase>);
-    return message_parser_.ParseMessage(message, [&](const auto&
-                                                         parsed_message) {
-      if constexpr (CanDispatch<Subclass, decltype(parsed_message)>::value) {
-        return absl::down_cast<Subclass*>(this)->OnControlMessage(
-            parsed_message);
+  template <typename StreamClass>
+  static absl::Status DispatchControlMessage(
+      StreamClass& stream, const MoqtControlMessageParser& parser,
+      const MoqtRawControlMessage& message, absl::string_view stream_type) {
+    static_assert(!std::is_same_v<StreamClass, MoqtBidiStreamBase>);
+    return parser.ParseMessage(message, [&](const auto& parsed_message) {
+      if constexpr (CanDispatch<StreamClass, decltype(parsed_message)>::value) {
+        return stream.OnControlMessage(parsed_message);
       } else {
         return absl::InvalidArgumentError(
             absl::StrCat("Received an unexpected message of type ",
@@ -157,8 +173,6 @@
   }
 
  private:
-  friend class test::MoqtBidiStreamTestWrapper;
-
   // CanDispatch<S, M> indicates whether `S` has a method with signature
   //     absl::Status OnControlMessage(const M&);
   template <typename Subclass, typename Message, typename = void>
@@ -169,13 +183,6 @@
                          decltype(std::declval<Subclass>().OnControlMessage(
                              std::declval<Message>())),
                          absl::Status>>> : std::true_type {};
-
-  MoqtFramer* absl_nonnull framer_;
-  std::unique_ptr<MoqtControlStreamParser> absl_nullable stream_parser_;
-  MoqtControlMessageParser message_parser_;
-  MoqtControlMessageQueue outgoing_message_queue_;
-  BidiStreamDeletedCallback stream_deleted_callback_;
-  SessionErrorCallback session_error_callback_;
 };
 
 }  // namespace moqt
diff --git a/quiche/quic/moqt/moqt_bidi_stream_test.cc b/quiche/quic/moqt/moqt_bidi_stream_test.cc
index bb85966..9b8dc62 100644
--- a/quiche/quic/moqt/moqt_bidi_stream_test.cc
+++ b/quiche/quic/moqt/moqt_bidi_stream_test.cc
@@ -32,7 +32,8 @@
   void OnStreamBound() override {}
   absl::Status OnRawControlMessage(
       const MoqtRawControlMessage& message) override {
-    return DispatchControlMessage<TestMoqtBidiStream>(message, "test");
+    return ControlMessageDispatcher::DispatchControlMessage(
+        *this, message_parser(), message, "test");
   }
   int ok_received() const { return ok_received_; }
 
diff --git a/quiche/quic/moqt/moqt_namespace_stream.cc b/quiche/quic/moqt/moqt_namespace_stream.cc
index 7eabe91..ec3668d 100644
--- a/quiche/quic/moqt/moqt_namespace_stream.cc
+++ b/quiche/quic/moqt/moqt_namespace_stream.cc
@@ -37,8 +37,8 @@
 }
 absl::Status MoqtNamespaceSubscriberStream::OnRawControlMessage(
     const MoqtRawControlMessage& message) {
-  return DispatchControlMessage<MoqtNamespaceSubscriberStream>(
-      message, "namespace subscriber");
+  return ControlMessageDispatcher::DispatchControlMessage(
+      *this, message_parser(), message, "namespace subscriber");
 }
 
 void MoqtNamespaceSubscriberStream::OnStreamBound() {
@@ -269,8 +269,8 @@
 
 absl::Status MoqtNamespacePublisherStream::OnRawControlMessage(
     const MoqtRawControlMessage& message) {
-  return DispatchControlMessage<MoqtNamespacePublisherStream>(
-      message, "namespace publisher");
+  return ControlMessageDispatcher::DispatchControlMessage(
+      *this, message_parser(), message, "namespace publisher");
 }
 
 absl::Status MoqtNamespacePublisherStream::OnControlMessage(
diff --git a/quiche/quic/moqt/moqt_session.cc b/quiche/quic/moqt/moqt_session.cc
index 3e34f70..8db77b3 100644
--- a/quiche/quic/moqt/moqt_session.cc
+++ b/quiche/quic/moqt/moqt_session.cc
@@ -885,7 +885,8 @@
 
 absl::Status MoqtSession::ControlStream::OnRawControlMessage(
     const MoqtRawControlMessage& message) {
-  return DispatchControlMessage<ControlStream>(message, "control");
+  return ControlMessageDispatcher::DispatchControlMessage(
+      *this, message_parser(), message, "control");
 }
 
 absl::Status MoqtSession::ControlStream::OnControlMessage(