aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/csharp/Grpc.Core
diff options
context:
space:
mode:
authorGravatar Jan Tattermusch <jtattermusch@users.noreply.github.com>2017-08-10 11:23:59 +0200
committerGravatar GitHub <noreply@github.com>2017-08-10 11:23:59 +0200
commita48f879aef6d33b69b544ed971c9f45b9cae3658 (patch)
treefc73f29cccac0064834d4c0bc0552cf06ddb9d0d /src/csharp/Grpc.Core
parent0fcd99e1682b15699af43b4ed4390e8be78aafb0 (diff)
parent9dc182161dfb35ed8f7bb45150bb53e097fcebe1 (diff)
Merge pull request #12120 from jtattermusch/csharp_rpcexception
Add Trailers property to RpcException
Diffstat (limited to 'src/csharp/Grpc.Core')
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCall.cs8
-rw-r--r--src/csharp/Grpc.Core/Internal/ServerCallHandler.cs19
-rw-r--r--src/csharp/Grpc.Core/RpcException.cs28
3 files changed, 46 insertions, 9 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/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
index 36702a3fab..6019f8e793 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
@@ -76,7 +76,7 @@ namespace Grpc.Core.Internal
{
Logger.Warning(e, "Exception occured in handler.");
}
- status = HandlerUtils.StatusFromException(e);
+ status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
}
try
{
@@ -133,7 +133,7 @@ namespace Grpc.Core.Internal
{
Logger.Warning(e, "Exception occured in handler.");
}
- status = HandlerUtils.StatusFromException(e);
+ status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
}
try
@@ -191,7 +191,7 @@ namespace Grpc.Core.Internal
{
Logger.Warning(e, "Exception occured in handler.");
}
- status = HandlerUtils.StatusFromException(e);
+ status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
}
try
@@ -247,7 +247,7 @@ namespace Grpc.Core.Internal
{
Logger.Warning(e, "Exception occured in handler.");
}
- status = HandlerUtils.StatusFromException(e);
+ status = HandlerUtils.GetStatusFromExceptionAndMergeTrailers(e, context.ResponseTrailers);
}
try
{
@@ -292,11 +292,20 @@ namespace Grpc.Core.Internal
internal static class HandlerUtils
{
- public static Status StatusFromException(Exception e)
+ public static Status GetStatusFromExceptionAndMergeTrailers(Exception e, Metadata callContextResponseTrailers)
{
var rpcException = e as RpcException;
if (rpcException != null)
{
+ // There are two sources of metadata entries on the server-side:
+ // 1. serverCallContext.ResponseTrailers
+ // 2. trailers in RpcException thrown by user code in server side handler.
+ // As metadata allows duplicate keys, the logical thing to do is
+ // to just merge trailers from RpcException into serverCallContext.ResponseTrailers.
+ foreach (var entry in rpcException.Trailers)
+ {
+ callContextResponseTrailers.Add(entry);
+ }
// use the status thrown by handler.
return rpcException.Status;
}
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;
+ }
+ }
}
}