blob: 5d22d77ada8216bcdf2ab4020b73b026efb2072e [file] [log] [blame]
Jan Tattermuschb2b54fe2017-06-27 14:37:01 +02001#region Copyright notice and license
2
3// Copyright 2015-2016 gRPC authors.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#endregion
18
19using System;
20using System.Collections.Generic;
21using System.IO;
22using System.Linq;
23using System.Threading;
24using System.Threading.Tasks;
25using Google.Protobuf;
26using Grpc.Core;
27using Grpc.Core.Utils;
28using Grpc.Testing;
29using NUnit.Framework;
30
31namespace Grpc.IntegrationTesting
32{
33 /// <summary>
34 /// Shows how to attach custom error details as a binary trailer.
35 /// </summary>
36 public class CustomErrorDetailsTest
37 {
38 const string DebugInfoTrailerName = "debug-info-bin";
39 const string ExceptionDetail = "Exception thrown on purpose.";
40 const string Host = "localhost";
41 Server server;
42 Channel channel;
43 TestService.TestServiceClient client;
44
Jan Tattermuschc152d662017-08-09 09:24:20 +020045 [OneTimeSetUp]
Jan Tattermuschb2b54fe2017-06-27 14:37:01 +020046 public void Init()
47 {
48 // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
49 server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
50 {
51 Services = { TestService.BindService(new CustomErrorDetailsTestServiceImpl()) },
52 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
53 };
54 server.Start();
55
56 channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
57 client = new TestService.TestServiceClient(channel);
58 }
59
Jan Tattermuschc152d662017-08-09 09:24:20 +020060 [OneTimeTearDown]
Jan Tattermuschb2b54fe2017-06-27 14:37:01 +020061 public void Cleanup()
62 {
63 channel.ShutdownAsync().Wait();
64 server.ShutdownAsync().Wait();
65 }
66
67 [Test]
Jan Tattermusch40ea2d32017-08-09 10:23:15 +020068 public async Task ErrorDetailsFromCallObject()
Jan Tattermuschb2b54fe2017-06-27 14:37:01 +020069 {
70 var call = client.UnaryCallAsync(new SimpleRequest { ResponseSize = 10 });
71
72 try
73 {
74 await call.ResponseAsync;
75 Assert.Fail();
76 }
77 catch (RpcException e)
78 {
79 Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
80 var debugInfo = GetDebugInfo(call.GetTrailers());
81 Assert.AreEqual(debugInfo.Detail, ExceptionDetail);
82 Assert.IsNotEmpty(debugInfo.StackEntries);
83 }
84 }
85
Jan Tattermusch40ea2d32017-08-09 10:23:15 +020086 [Test]
87 public async Task ErrorDetailsFromRpcException()
88 {
89 try
90 {
91 await client.UnaryCallAsync(new SimpleRequest { ResponseSize = 10 });
92 Assert.Fail();
93 }
94 catch (RpcException e)
95 {
96 Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
97 var debugInfo = GetDebugInfo(e.Trailers);
98 Assert.AreEqual(debugInfo.Detail, ExceptionDetail);
99 Assert.IsNotEmpty(debugInfo.StackEntries);
100 }
101 }
102
103 private static DebugInfo GetDebugInfo(Metadata trailers)
Jan Tattermuschb2b54fe2017-06-27 14:37:01 +0200104 {
105 var entry = trailers.First((e) => e.Key == DebugInfoTrailerName);
106 return DebugInfo.Parser.ParseFrom(entry.ValueBytes);
107 }
108
109 private class CustomErrorDetailsTestServiceImpl : TestService.TestServiceBase
110 {
Jan Tattermuschc152d662017-08-09 09:24:20 +0200111 public override Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context)
Jan Tattermuschb2b54fe2017-06-27 14:37:01 +0200112 {
113 try
114 {
115 throw new ArgumentException(ExceptionDetail);
116 }
117 catch (Exception e)
118 {
119 // Fill debug info with some structured details about the failure.
120 var debugInfo = new DebugInfo();
121 debugInfo.Detail = e.Message;
122 debugInfo.StackEntries.AddRange(e.StackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None));
123 context.ResponseTrailers.Add(DebugInfoTrailerName, debugInfo.ToByteArray());
124 throw new RpcException(new Status(StatusCode.Unknown, "The handler threw exception."));
125 }
126 }
127 }
128 }
129}