Simplify SctpDataChannel construction a bit.

This moves SctpDataChannel construction a step closer to RAII by moving the error checks out of SctpDataChannel::Init() and not construct an SctpDataChannel instance unless error checks have been done first in SctpDataChannel::Create.

Ideally the Init() method shouldn't be needed but there is test code that constructs an SctpDataChannel instance without running the Init()
steps but they're required by the SctpDataChannel::Create() path.

Bug: none
Change-Id: I8498693063c28355f901d27c4fe7bd45b7d4be26
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/295860
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39467}
diff --git a/pc/sctp_data_channel.cc b/pc/sctp_data_channel.cc
index 0c66e77..cb7c1c3 100644
--- a/pc/sctp_data_channel.cc
+++ b/pc/sctp_data_channel.cc
@@ -101,6 +101,23 @@
   }
 }
 
+bool InternalDataChannelInit::IsValid() const {
+  if (id < -1)
+    return false;
+
+  if (maxRetransmits.has_value() && maxRetransmits.value() < 0)
+    return false;
+
+  if (maxRetransmitTime.has_value() && maxRetransmitTime.value() < 0)
+    return false;
+
+  // Only one of these can be set.
+  if (maxRetransmits.has_value() && maxRetransmitTime.has_value())
+    return false;
+
+  return true;
+}
+
 bool SctpSidAllocator::AllocateSid(rtc::SSLRole role, int* sid) {
   int potential_sid = (role == rtc::SSL_CLIENT) ? 0 : 1;
   while (!IsSidAvailable(potential_sid)) {
@@ -145,11 +162,17 @@
     const InternalDataChannelInit& config,
     rtc::Thread* signaling_thread,
     rtc::Thread* network_thread) {
-  auto channel = rtc::make_ref_counted<SctpDataChannel>(
-      config, std::move(controller), label, signaling_thread, network_thread);
-  if (!channel->Init()) {
+  RTC_DCHECK(controller);
+
+  if (!config.IsValid()) {
+    RTC_LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to "
+                         "invalid DataChannelInit.";
     return nullptr;
   }
+
+  auto channel = rtc::make_ref_counted<SctpDataChannel>(
+      config, std::move(controller), label, signaling_thread, network_thread);
+  channel->Init();
   return channel;
 }
 
@@ -176,22 +199,7 @@
       controller_(std::move(controller)) {
   RTC_DCHECK_RUN_ON(signaling_thread_);
   RTC_UNUSED(network_thread_);
-}
-
-bool SctpDataChannel::Init() {
-  RTC_DCHECK_RUN_ON(signaling_thread_);
-  if (config_.id < -1 ||
-      (config_.maxRetransmits && *config_.maxRetransmits < 0) ||
-      (config_.maxRetransmitTime && *config_.maxRetransmitTime < 0)) {
-    RTC_LOG(LS_ERROR) << "Failed to initialize the SCTP data channel due to "
-                         "invalid DataChannelInit.";
-    return false;
-  }
-  if (config_.maxRetransmits && config_.maxRetransmitTime) {
-    RTC_LOG(LS_ERROR)
-        << "maxRetransmits and maxRetransmitTime should not be both set.";
-    return false;
-  }
+  RTC_DCHECK(config_.IsValid());
 
   switch (config_.open_handshake_role) {
     case webrtc::InternalDataChannelInit::kNone:  // pre-negotiated
@@ -204,6 +212,10 @@
       handshake_state_ = kHandshakeShouldSendAck;
       break;
   }
+}
+
+void SctpDataChannel::Init() {
+  RTC_DCHECK_RUN_ON(signaling_thread_);
 
   // Try to connect to the transport in case the transport channel already
   // exists.
@@ -214,7 +226,6 @@
   // This has to be done async because the upper layer objects (e.g.
   // Chrome glue and WebKit) are not wired up properly until after this
   // function returns.
-  RTC_DCHECK(controller_);
   if (controller_->ReadyToSendData()) {
     AddRef();
     absl::Cleanup release = [this] { Release(); };
@@ -224,8 +235,6 @@
         OnTransportReady(true);
     });
   }
-
-  return true;
 }
 
 SctpDataChannel::~SctpDataChannel() {
diff --git a/pc/sctp_data_channel.h b/pc/sctp_data_channel.h
index f42818f..91daaf7 100644
--- a/pc/sctp_data_channel.h
+++ b/pc/sctp_data_channel.h
@@ -71,6 +71,11 @@
   // The default role is kOpener because the default `negotiated` is false.
   InternalDataChannelInit() : open_handshake_role(kOpener) {}
   explicit InternalDataChannelInit(const DataChannelInit& base);
+
+  // Does basic validation to determine if a data channel instance can be
+  // constructed using the configuration.
+  bool IsValid() const;
+
   OpenHandshakeRole open_handshake_role;
 };
 
@@ -237,7 +242,7 @@
     kHandshakeReady
   };
 
-  bool Init();
+  void Init();
   void UpdateState();
   void SetState(DataState state);
   void DisconnectFromTransport();