Skip to content

Commit 4daaf07

Browse files
authored
Set unexpected errors in Status.DebugException (grpc#947)
1 parent e02fd18 commit 4daaf07

File tree

6 files changed

+49
-9
lines changed

6 files changed

+49
-9
lines changed

src/Grpc.AspNetCore.Server/Internal/HttpContextServerCallContext.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,10 @@ private void ProcessHandlerError(Exception ex, string method)
189189
GrpcServerLog.ErrorExecutingServiceMethod(Logger, method, ex);
190190

191191
var message = ErrorMessageHelper.BuildErrorMessage("Exception was thrown by handler.", ex, Options.EnableDetailedErrors);
192-
_status = new Status(StatusCode.Unknown, message);
192+
193+
// Note that the exception given to status won't be returned to the client.
194+
// It is still useful to set in case an interceptor accesses the status on the server.
195+
_status = new Status(StatusCode.Unknown, message, ex);
193196
}
194197

195198
// Don't update trailers if request has exceeded deadline/aborted

src/Grpc.Net.Client/Internal/GrpcCall.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ private void ResolveException(Exception ex, [NotNull] out Status? status, out Ex
610610
{
611611
var exceptionMessage = CommonGrpcProtocolHelpers.ConvertToRpcExceptionMessage(ex);
612612

613-
status = new Status(StatusCode.Internal, "Error starting gRPC call. " + exceptionMessage);
613+
status = new Status(StatusCode.Internal, "Error starting gRPC call. " + exceptionMessage, ex);
614614
resolvedException = CreateRpcException(status.Value);
615615
}
616616
}

src/Grpc.Net.Client/Internal/GrpcProtocolHelpers.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ public static Status GetResponseStatus(HttpResponseMessage httpResponse)
315315
catch (Exception ex)
316316
{
317317
// Handle error from parsing badly formed status
318-
status = new Status(StatusCode.Cancelled, ex.Message);
318+
status = new Status(StatusCode.Cancelled, ex.Message, ex);
319319
}
320320

321321
return status.Value;

test/Grpc.AspNetCore.Server.Tests/CallHandlerTests.cs

+40-6
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
using Grpc.AspNetCore.Server.Tests.Infrastructure;
3030
using Grpc.AspNetCore.Server.Tests.TestObjects;
3131
using Grpc.Core;
32-
using Grpc.Net.Compression;
3332
using Grpc.Shared.Server;
3433
using Grpc.Tests.Shared;
3534
using Microsoft.AspNetCore.Http;
@@ -176,7 +175,26 @@ public async Task ProtocolValidation_IISHttp2Protocol_Success()
176175
Assert.IsNull(log);
177176
}
178177

179-
private static ServerCallHandlerBase<TestService, TestMessage, TestMessage> CreateHandler(MethodType methodType, ILoggerFactory? loggerFactory = null)
178+
[Test]
179+
public async Task StatusDebugException_ErrorInHandler_SetInDebugException()
180+
{
181+
// Arrange
182+
var ex = new Exception("Test exception");
183+
var httpContext = HttpContextHelpers.CreateContext();
184+
var call = CreateHandler(MethodType.ClientStreaming, handlerAction: () => throw ex);
185+
186+
// Act
187+
await call.HandleCallAsync(httpContext).DefaultTimeout();
188+
189+
// Assert
190+
var serverCallContext = httpContext.Features.Get<IServerCallContextFeature>();
191+
Assert.AreEqual(ex, serverCallContext.ServerCallContext.Status.DebugException);
192+
}
193+
194+
private static ServerCallHandlerBase<TestService, TestMessage, TestMessage> CreateHandler(
195+
MethodType methodType,
196+
ILoggerFactory? loggerFactory = null,
197+
Action? handlerAction = null)
180198
{
181199
var method = new Method<TestMessage, TestMessage>(methodType, "test", "test", _marshaller, _marshaller);
182200

@@ -185,31 +203,47 @@ private static ServerCallHandlerBase<TestService, TestMessage, TestMessage> Crea
185203
case MethodType.Unary:
186204
return new UnaryServerCallHandler<TestService, TestMessage, TestMessage>(
187205
new UnaryServerMethodInvoker<TestService, TestMessage, TestMessage>(
188-
(service, reader, context) => Task.FromResult(new TestMessage()),
206+
(service, reader, context) =>
207+
{
208+
handlerAction?.Invoke();
209+
return Task.FromResult(new TestMessage());
210+
},
189211
method,
190212
HttpContextServerCallContextHelper.CreateMethodOptions(),
191213
new TestGrpcServiceActivator<TestService>()),
192214
loggerFactory ?? NullLoggerFactory.Instance);
193215
case MethodType.ClientStreaming:
194216
return new ClientStreamingServerCallHandler<TestService, TestMessage, TestMessage>(
195217
new ClientStreamingServerMethodInvoker<TestService, TestMessage, TestMessage>(
196-
(service, reader, context) => Task.FromResult(new TestMessage()),
218+
(service, reader, context) =>
219+
{
220+
handlerAction?.Invoke();
221+
return Task.FromResult(new TestMessage());
222+
},
197223
method,
198224
HttpContextServerCallContextHelper.CreateMethodOptions(),
199225
new TestGrpcServiceActivator<TestService>()),
200226
loggerFactory ?? NullLoggerFactory.Instance);
201227
case MethodType.ServerStreaming:
202228
return new ServerStreamingServerCallHandler<TestService, TestMessage, TestMessage>(
203229
new ServerStreamingServerMethodInvoker<TestService, TestMessage, TestMessage>(
204-
(service, request, writer, context) => Task.FromResult(new TestMessage()),
230+
(service, request, writer, context) =>
231+
{
232+
handlerAction?.Invoke();
233+
return Task.FromResult(new TestMessage());
234+
},
205235
method,
206236
HttpContextServerCallContextHelper.CreateMethodOptions(),
207237
new TestGrpcServiceActivator<TestService>()),
208238
loggerFactory ?? NullLoggerFactory.Instance);
209239
case MethodType.DuplexStreaming:
210240
return new DuplexStreamingServerCallHandler<TestService, TestMessage, TestMessage>(
211241
new DuplexStreamingServerMethodInvoker<TestService, TestMessage, TestMessage>(
212-
(service, reader, writer, context) => Task.CompletedTask,
242+
(service, reader, writer, context) =>
243+
{
244+
handlerAction?.Invoke();
245+
return Task.CompletedTask;
246+
},
213247
method,
214248
HttpContextServerCallContextHelper.CreateMethodOptions(),
215249
new TestGrpcServiceActivator<TestService>()),

test/Grpc.Net.Client.Tests/CompressionTests.cs

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public async Task AsyncUnaryCall_UnknownCompressMetadataSentWithRequest_ThrowsEr
8484
var ex = await ExceptionAssert.ThrowsAsync<RpcException>(() => call.ResponseAsync).DefaultTimeout();
8585
Assert.AreEqual(StatusCode.Internal, ex.StatusCode);
8686
Assert.AreEqual("Error starting gRPC call. InvalidOperationException: Could not find compression provider for 'not-supported'.", ex.Status.Detail);
87+
Assert.AreEqual("Could not find compression provider for 'not-supported'.", ex.Status.DebugException.Message);
8788
}
8889

8990
[TestCase(true)]

test/Grpc.Net.Client.Tests/ResponseAsyncTests.cs

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public async Task AsyncUnaryCall_ErrorSendingRequest_ThrowsError()
136136
// Assert
137137
Assert.AreEqual(StatusCode.Internal, ex.StatusCode);
138138
Assert.AreEqual("Error starting gRPC call. Exception: An error!", ex.Status.Detail);
139+
Assert.AreEqual("An error!", ex.Status.DebugException.Message);
139140
Assert.AreEqual(StatusCode.Internal, call.GetStatus().StatusCode);
140141
}
141142

@@ -156,6 +157,7 @@ public async Task AsyncUnaryCall_ErrorSendingRequest_ThrowsErrorWithInnerExcepti
156157
// Assert
157158
Assert.AreEqual(StatusCode.Internal, ex.StatusCode);
158159
Assert.AreEqual("Error starting gRPC call. Exception: An error! Exception: Nested error!", ex.Status.Detail);
160+
Assert.AreEqual("Nested error!", ex.Status.DebugException.InnerException!.Message);
159161
Assert.AreEqual(StatusCode.Internal, call.GetStatus().StatusCode);
160162
}
161163

0 commit comments

Comments
 (0)