From e64825c302e1eacac43d2b21049fddaa25e11902 Mon Sep 17 00:00:00 2001
From: Jan Tattermusch <jtattermusch@google.com>
Date: Tue, 8 Aug 2017 16:10:23 +0200
Subject: [PATCH] Populate trailers in RpcException

---
 src/csharp/Grpc.Core/Internal/AsyncCall.cs |  8 +++----
 src/csharp/Grpc.Core/RpcException.cs       | 28 ++++++++++++++++++++++
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index 6e6ca7cd53..17109de587 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -329,7 +329,7 @@ namespace Grpc.Core.Internal
 
         protected override Exception GetRpcExceptionClientOnly()
         {
-            return new RpcException(finishedStatus.Value.Status);
+            return new RpcException(finishedStatus.Value.Status, finishedStatus.Value.Trailers);
         }
 
         protected override Task CheckSendAllowedOrEarlyResult()
@@ -348,7 +348,7 @@ namespace Grpc.Core.Internal
                 // Writing after the call has finished is not a programming error because server can close
                 // the call anytime, so don't throw directly, but let the write task finish with an error.
                 var tcs = new TaskCompletionSource<object>();
-                tcs.SetException(new RpcException(finishedStatus.Value.Status));
+                tcs.SetException(new RpcException(finishedStatus.Value.Status, finishedStatus.Value.Trailers));
                 return tcs.Task;
             }
 
@@ -468,7 +468,7 @@ namespace Grpc.Core.Internal
             var status = receivedStatus.Status;
             if (status.StatusCode != StatusCode.OK)
             {
-                unaryResponseTcs.SetException(new RpcException(status));
+                unaryResponseTcs.SetException(new RpcException(status, receivedStatus.Trailers));
                 return;
             }
 
@@ -506,7 +506,7 @@ namespace Grpc.Core.Internal
             var status = receivedStatus.Status;
             if (status.StatusCode != StatusCode.OK)
             {
-                streamingResponseCallFinishedTcs.SetException(new RpcException(status));
+                streamingResponseCallFinishedTcs.SetException(new RpcException(status, receivedStatus.Trailers));
                 return;
             }
 
diff --git a/src/csharp/Grpc.Core/RpcException.cs b/src/csharp/Grpc.Core/RpcException.cs
index 01b9e4fb1a..d2c912e73a 100644
--- a/src/csharp/Grpc.Core/RpcException.cs
+++ b/src/csharp/Grpc.Core/RpcException.cs
@@ -17,6 +17,7 @@
 #endregion
 
 using System;
+using Grpc.Core.Utils;
 
 namespace Grpc.Core
 {
@@ -26,6 +27,7 @@ namespace Grpc.Core
     public class RpcException : Exception
     {
         private readonly Status status;
+        private readonly Metadata trailers;
 
         /// <summary>
         /// Creates a new <c>RpcException</c> associated with given status.
@@ -34,6 +36,7 @@ namespace Grpc.Core
         public RpcException(Status status) : base(status.ToString())
         {
             this.status = status;
+            this.trailers = Metadata.Empty;
         }
 
         /// <summary>
@@ -44,6 +47,18 @@ namespace Grpc.Core
         public RpcException(Status status, string message) : base(message)
         {
             this.status = status;
+            this.trailers = Metadata.Empty;
+        }
+
+        /// <summary>
+        /// Creates a new <c>RpcException</c> associated with given status and trailing response metadata.
+        /// </summary>
+        /// <param name="status">Resulting status of a call.</param>
+        /// <param name="trailers">Response trailing metadata.</param> 
+        public RpcException(Status status, Metadata trailers) : base(status.ToString())
+        {
+            this.status = status;
+            this.trailers = GrpcPreconditions.CheckNotNull(trailers);
         }
 
         /// <summary>
@@ -56,5 +71,18 @@ namespace Grpc.Core
                 return status;
             }
         }
+
+        /// <summary>
+        /// Gets the call trailing metadata.
+        /// Trailers only have meaningful content for client-side calls (in which case they represent the trailing metadata sent by the server when closing the call).
+        /// Instances of <c>RpcException</c> thrown by the server-side part of the stack will have trailers always set to empty.
+        /// </summary>
+        public Metadata Trailers
+        {
+            get
+            {
+                return trailers;
+            }
+        }
     }
 }
-- 
GitLab