Reland "Set session error if SetLocal/RemoteDescription ever fails"

Original change's description:
> Set session error if SetLocal/RemoteDescription ever fails
> 
> This changes SetLocalDescription/SetRemoteDescription to set a
> session error which will cause any future calls to fail early if
> there is an error when applying a session description.
> 
> This is needed since until better error recovery is implemented
> failing a call to SetLocalDescription or SetRemoteDescription
> could leave the PeerConnection in an inconsistent state.
> 
> Bug: chromium:800775
> Change-Id: If06fd73d6e902af15d072dc562bbe830d3b11ad5
> Reviewed-on: https://webrtc-review.googlesource.com/54061
> Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
> Commit-Queue: Steve Anton <steveanton@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#22061}

Bug: chromium:800775
Change-Id: I0016108264e013452e9d34239c012baf23240e99
Reviewed-on: https://webrtc-review.googlesource.com/54720
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22067}
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index f04b6e5..73f79c8 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -561,6 +561,17 @@
   return false;
 }
 
+// Generates a string error message for SetLocalDescription/SetRemoteDescription
+// from an RTCError.
+std::string GetSetDescriptionErrorMessage(cricket::ContentSource source,
+                                          SdpType type,
+                                          const RTCError& error) {
+  std::ostringstream oss;
+  oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote")
+      << " " << SdpTypeToString(type) << " sdp: " << error.message();
+  return oss.str();
+}
+
 }  // namespace
 
 // Upon completion, posts a task to execute the callback of the
@@ -1733,9 +1744,15 @@
 
 void PeerConnection::SetLocalDescription(
     SetSessionDescriptionObserver* observer,
-    SessionDescriptionInterface* desc) {
+    SessionDescriptionInterface* desc_ptr) {
   TRACE_EVENT0("webrtc", "PeerConnection::SetLocalDescription");
 
+  // The SetLocalDescription contract is that we take ownership of the session
+  // description regardless of the outcome, so wrap it in a unique_ptr right
+  // away. Ideally, SetLocalDescription's signature will be changed to take the
+  // description as a unique_ptr argument to formalize this agreement.
+  std::unique_ptr<SessionDescriptionInterface> desc(desc_ptr);
+
   if (!observer) {
     RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL.";
     return;
@@ -1746,17 +1763,39 @@
     return;
   }
 
-  SdpType type = desc->GetType();
+  // If a session error has occurred the PeerConnection is in a possibly
+  // inconsistent state so fail right away.
+  if (session_error() != SessionError::kNone) {
+    std::string error_message = GetSessionErrorMsg();
+    RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message;
+    PostSetSessionDescriptionFailure(observer, std::move(error_message));
+    return;
+  }
 
-  RTCError error = ApplyLocalDescription(rtc::WrapUnique(desc));
+  RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL);
+  if (!error.ok()) {
+    std::string error_message = GetSetDescriptionErrorMessage(
+        cricket::CS_LOCAL, desc->GetType(), error);
+    RTC_LOG(LS_ERROR) << error_message;
+    PostSetSessionDescriptionFailure(observer, std::move(error_message));
+    return;
+  }
+
+  // Grab the description type before moving ownership to ApplyLocalDescription,
+  // which may destroy it before returning.
+  const SdpType type = desc->GetType();
+
+  error = ApplyLocalDescription(std::move(desc));
   // |desc| may be destroyed at this point.
 
   if (!error.ok()) {
-    std::ostringstream oss;
-    oss << "Failed to set local " << SdpTypeToString(type)
-        << " sdp: " << error.message();
-    std::string error_message = oss.str();
-    RTC_LOG(LS_ERROR) << error_message << " (" << error.type() << ")";
+    // If ApplyLocalDescription fails, the PeerConnection could be in an
+    // inconsistent state, so act conservatively here and set the session error
+    // so that future calls to SetLocalDescription/SetRemoteDescription fail.
+    SetSessionError(SessionError::kContent, error.message());
+    std::string error_message =
+        GetSetDescriptionErrorMessage(cricket::CS_LOCAL, type, error);
+    RTC_LOG(LS_ERROR) << error_message;
     PostSetSessionDescriptionFailure(observer, std::move(error_message));
     return;
   }
@@ -1788,11 +1827,6 @@
   RTC_DCHECK_RUN_ON(signaling_thread());
   RTC_DCHECK(desc);
 
-  RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL);
-  if (!error.ok()) {
-    return error;
-  }
-
   // Update stats here so that we have the most recent stats for tracks and
   // streams that might be removed by updating the session description.
   stats_->UpdateStats(kStatsOutputLevelStandard);
@@ -1867,7 +1901,7 @@
     RemoveUnusedChannels(local_description()->description());
   }
 
-  error = UpdateSessionState(type, cricket::CS_LOCAL);
+  RTCError error = UpdateSessionState(type, cricket::CS_LOCAL);
   if (!error.ok()) {
     return error;
   }
@@ -1976,23 +2010,48 @@
     return;
   }
 
-  const SdpType type = desc->GetType();
+  // If a session error has occurred the PeerConnection is in a possibly
+  // inconsistent state so fail right away.
+  if (session_error() != SessionError::kNone) {
+    std::string error_message = GetSessionErrorMsg();
+    RTC_LOG(LS_ERROR) << "SetRemoteDescription: " << error_message;
+    observer->OnSetRemoteDescriptionComplete(
+        RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
+    return;
+  }
 
-  RTCError error = ApplyRemoteDescription(std::move(desc));
-  // |desc| may be destroyed at this point.
-
+  RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE);
   if (!error.ok()) {
-    std::ostringstream oss;
-    oss << "Failed to set remote " << SdpTypeToString(type)
-        << " sdp: " << error.message();
-    std::string error_message = oss.str();
-    RTC_LOG(LS_ERROR) << error_message << " (" << error.type() << ")";
+    std::string error_message = GetSetDescriptionErrorMessage(
+        cricket::CS_REMOTE, desc->GetType(), error);
+    RTC_LOG(LS_ERROR) << error_message;
     observer->OnSetRemoteDescriptionComplete(
         RTCError(error.type(), std::move(error_message)));
     return;
   }
 
-  if (remote_description()->GetType() == SdpType::kAnswer) {
+  // Grab the description type before moving ownership to
+  // ApplyRemoteDescription, which may destroy it before returning.
+  const SdpType type = desc->GetType();
+
+  error = ApplyRemoteDescription(std::move(desc));
+  // |desc| may be destroyed at this point.
+
+  if (!error.ok()) {
+    // If ApplyRemoteDescription fails, the PeerConnection could be in an
+    // inconsistent state, so act conservatively here and set the session error
+    // so that future calls to SetLocalDescription/SetRemoteDescription fail.
+    SetSessionError(SessionError::kContent, error.message());
+    std::string error_message =
+        GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type, error);
+    RTC_LOG(LS_ERROR) << error_message;
+    observer->OnSetRemoteDescriptionComplete(
+        RTCError(error.type(), std::move(error_message)));
+    return;
+  }
+  RTC_DCHECK(remote_description());
+
+  if (type == SdpType::kAnswer) {
     // TODO(deadbeef): We already had to hop to the network thread for
     // MaybeStartGathering...
     network_thread()->Invoke<void>(
@@ -2036,11 +2095,6 @@
   RTC_DCHECK_RUN_ON(signaling_thread());
   RTC_DCHECK(desc);
 
-  RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE);
-  if (!error.ok()) {
-    return error;
-  }
-
   // Update stats here so that we have the most recent stats for tracks and
   // streams that might be removed by updating the session description.
   stats_->UpdateStats(kStatsOutputLevelStandard);
@@ -2093,7 +2147,7 @@
 
   // NOTE: Candidates allocation will be initiated only when SetLocalDescription
   // is called.
-  error = UpdateSessionState(type, cricket::CS_REMOTE);
+  RTCError error = UpdateSessionState(type, cricket::CS_REMOTE);
   if (!error.ok()) {
     return error;
   }
@@ -4499,10 +4553,7 @@
   // descriptions.
   error = PushdownMediaDescription(type, source);
   if (!error.ok()) {
-    SetSessionError(SessionError::kContent, error.message());
-  }
-  if (session_error() != SessionError::kNone) {
-    LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg());
+    return error;
   }
 
   return RTCError::OK();