1 #region Copyright notice and license 2 3 // Copyright 2015 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 19 using System; 20 using System.Collections.Generic; 21 using System.Diagnostics; 22 using System.Linq; 23 using System.Threading; 24 using System.Threading.Tasks; 25 using Grpc.Core; 26 using Grpc.Core.Internal; 27 using Grpc.Core.Profiling; 28 using Grpc.Core.Utils; 29 using NUnit.Framework; 30 31 namespace Grpc.Core.Tests 32 { 33 public class ClientServerTest 34 { 35 const string Host = "127.0.0.1"; 36 37 MockServiceHelper helper; 38 Server server; 39 Channel channel; 40 41 [SetUp] Init()42 public void Init() 43 { 44 helper = new MockServiceHelper(Host); 45 server = helper.GetServer(); 46 server.Start(); 47 channel = helper.GetChannel(); 48 } 49 50 [TearDown] Cleanup()51 public void Cleanup() 52 { 53 channel.ShutdownAsync().Wait(); 54 server.ShutdownAsync().Wait(); 55 } 56 57 [Test] UnaryCall()58 public async Task UnaryCall() 59 { 60 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 61 { 62 return Task.FromResult(request); 63 }); 64 65 Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC")); 66 67 Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC")); 68 } 69 70 [Test] UnaryCall_ServerHandlerThrows()71 public void UnaryCall_ServerHandlerThrows() 72 { 73 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 74 { 75 throw new Exception("This was thrown on purpose by a test"); 76 }); 77 78 var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); 79 Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode); 80 81 var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); 82 Assert.AreEqual(StatusCode.Unknown, ex2.Status.StatusCode); 83 } 84 85 [Test] UnaryCall_ServerHandlerThrowsRpcException()86 public void UnaryCall_ServerHandlerThrowsRpcException() 87 { 88 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 89 { 90 throw new RpcException(new Status(StatusCode.Unauthenticated, "")); 91 }); 92 93 var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); 94 Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode); 95 Assert.AreEqual(0, ex.Trailers.Count); 96 97 var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); 98 Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode); 99 Assert.AreEqual(0, ex.Trailers.Count); 100 } 101 102 [Test] UnaryCall_ServerHandlerThrowsRpcExceptionWithTrailers()103 public void UnaryCall_ServerHandlerThrowsRpcExceptionWithTrailers() 104 { 105 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 106 { 107 var trailers = new Metadata { {"xyz", "xyz-value"} }; 108 throw new RpcException(new Status(StatusCode.Unauthenticated, ""), trailers); 109 }); 110 111 var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); 112 Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode); 113 Assert.AreEqual(1, ex.Trailers.Count); 114 Assert.AreEqual("xyz", ex.Trailers[0].Key); 115 Assert.AreEqual("xyz-value", ex.Trailers[0].Value); 116 117 var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); 118 Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode); 119 Assert.AreEqual(1, ex2.Trailers.Count); 120 Assert.AreEqual("xyz", ex2.Trailers[0].Key); 121 Assert.AreEqual("xyz-value", ex2.Trailers[0].Value); 122 } 123 124 [Test] UnaryCall_ServerHandlerSetsStatus()125 public void UnaryCall_ServerHandlerSetsStatus() 126 { 127 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 128 { 129 context.Status = new Status(StatusCode.Unauthenticated, ""); 130 return Task.FromResult(""); 131 }); 132 133 var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); 134 Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode); 135 Assert.AreEqual(0, ex.Trailers.Count); 136 137 var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); 138 Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode); 139 Assert.AreEqual(0, ex2.Trailers.Count); 140 } 141 142 [Test] UnaryCall_ServerHandlerSetsStatusAndTrailers()143 public void UnaryCall_ServerHandlerSetsStatusAndTrailers() 144 { 145 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 146 { 147 context.Status = new Status(StatusCode.Unauthenticated, ""); 148 context.ResponseTrailers.Add("xyz", "xyz-value"); 149 return Task.FromResult(""); 150 }); 151 152 var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); 153 Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode); 154 Assert.AreEqual(1, ex.Trailers.Count); 155 Assert.AreEqual("xyz", ex.Trailers[0].Key); 156 Assert.AreEqual("xyz-value", ex.Trailers[0].Value); 157 158 var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc")); 159 Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode); 160 Assert.AreEqual(1, ex2.Trailers.Count); 161 Assert.AreEqual("xyz", ex2.Trailers[0].Key); 162 Assert.AreEqual("xyz-value", ex2.Trailers[0].Value); 163 } 164 165 [Test] ClientStreamingCall()166 public async Task ClientStreamingCall() 167 { 168 helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) => 169 { 170 string result = ""; 171 await requestStream.ForEachAsync((request) => 172 { 173 result += request; 174 return TaskUtils.CompletedTask; 175 }); 176 await Task.Delay(100); 177 return result; 178 }); 179 180 var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); 181 await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" }); 182 Assert.AreEqual("ABC", await call.ResponseAsync); 183 184 Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); 185 Assert.IsNotNull(call.GetTrailers()); 186 } 187 188 [Test] ServerStreamingCall()189 public async Task ServerStreamingCall() 190 { 191 helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) => 192 { 193 await responseStream.WriteAllAsync(request.Split(new []{' '})); 194 context.ResponseTrailers.Add("xyz", ""); 195 }); 196 197 var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "A B C"); 198 CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync()); 199 200 Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); 201 Assert.AreEqual("xyz", call.GetTrailers()[0].Key); 202 } 203 204 [Test] ServerStreamingCall_EndOfStreamIsIdempotent()205 public async Task ServerStreamingCall_EndOfStreamIsIdempotent() 206 { 207 helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) => TaskUtils.CompletedTask); 208 209 var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), ""); 210 211 Assert.IsFalse(await call.ResponseStream.MoveNext()); 212 Assert.IsFalse(await call.ResponseStream.MoveNext()); 213 } 214 215 [Test] ServerStreamingCall_ErrorCanBeAwaitedTwice()216 public void ServerStreamingCall_ErrorCanBeAwaitedTwice() 217 { 218 helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) => 219 { 220 context.Status = new Status(StatusCode.InvalidArgument, ""); 221 return TaskUtils.CompletedTask; 222 }); 223 224 var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), ""); 225 226 var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext()); 227 Assert.AreEqual(StatusCode.InvalidArgument, ex.Status.StatusCode); 228 229 // attempting MoveNext again should result in throwing the same exception. 230 var ex2 = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext()); 231 Assert.AreEqual(StatusCode.InvalidArgument, ex2.Status.StatusCode); 232 } 233 234 [Test] ServerStreamingCall_TrailersFromMultipleSourcesGetConcatenated()235 public void ServerStreamingCall_TrailersFromMultipleSourcesGetConcatenated() 236 { 237 helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) => 238 { 239 context.ResponseTrailers.Add("xyz", "xyz-value"); 240 throw new RpcException(new Status(StatusCode.InvalidArgument, ""), new Metadata { {"abc", "abc-value"} }); 241 }); 242 243 var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), ""); 244 245 var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext()); 246 Assert.AreEqual(StatusCode.InvalidArgument, ex.Status.StatusCode); 247 Assert.AreEqual(2, call.GetTrailers().Count); 248 Assert.AreEqual(2, ex.Trailers.Count); 249 Assert.AreEqual("xyz", ex.Trailers[0].Key); 250 Assert.AreEqual("xyz-value", ex.Trailers[0].Value); 251 Assert.AreEqual("abc", ex.Trailers[1].Key); 252 Assert.AreEqual("abc-value", ex.Trailers[1].Value); 253 } 254 255 [Test] DuplexStreamingCall()256 public async Task DuplexStreamingCall() 257 { 258 helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) => 259 { 260 while (await requestStream.MoveNext()) 261 { 262 await responseStream.WriteAsync(requestStream.Current); 263 } 264 context.ResponseTrailers.Add("xyz", "xyz-value"); 265 }); 266 267 var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall()); 268 await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" }); 269 CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync()); 270 271 Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); 272 Assert.AreEqual("xyz-value", call.GetTrailers()[0].Value); 273 } 274 275 [Test] AsyncUnaryCall_EchoMetadata()276 public async Task AsyncUnaryCall_EchoMetadata() 277 { 278 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 279 { 280 foreach (Metadata.Entry metadataEntry in context.RequestHeaders) 281 { 282 if (metadataEntry.Key != "user-agent") 283 { 284 context.ResponseTrailers.Add(metadataEntry); 285 } 286 } 287 return Task.FromResult(""); 288 }); 289 290 var headers = new Metadata 291 { 292 { "ascii-header", "abcdefg" }, 293 { "binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff } } 294 }; 295 var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC"); 296 await call; 297 298 Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); 299 300 var trailers = call.GetTrailers(); 301 Assert.AreEqual(2, trailers.Count); 302 Assert.AreEqual(headers[0].Key, trailers[0].Key); 303 Assert.AreEqual(headers[0].Value, trailers[0].Value); 304 305 Assert.AreEqual(headers[1].Key, trailers[1].Key); 306 CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes); 307 } 308 309 [Test] UnknownMethodHandler()310 public void UnknownMethodHandler() 311 { 312 var nonexistentMethod = new Method<string, string>( 313 MethodType.Unary, 314 MockServiceHelper.ServiceName, 315 "NonExistentMethod", 316 Marshallers.StringMarshaller, 317 Marshallers.StringMarshaller); 318 319 var callDetails = new CallInvocationDetails<string, string>(channel, nonexistentMethod, new CallOptions()); 320 321 var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(callDetails, "abc")); 322 Assert.AreEqual(StatusCode.Unimplemented, ex.Status.StatusCode); 323 } 324 325 [Test] StatusDetailIsUtf8()326 public void StatusDetailIsUtf8() 327 { 328 // some japanese and chinese characters 329 var nonAsciiString = "\u30a1\u30a2\u30a3 \u62b5\u6297\u662f\u5f92\u52b3\u7684"; 330 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 331 { 332 context.Status = new Status(StatusCode.Unknown, nonAsciiString); 333 return Task.FromResult(""); 334 }); 335 336 var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); 337 Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode); 338 Assert.AreEqual(nonAsciiString, ex.Status.Detail); 339 } 340 341 [Test] ServerCallContext_PeerInfoPresent()342 public void ServerCallContext_PeerInfoPresent() 343 { 344 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 345 { 346 return Task.FromResult(context.Peer); 347 }); 348 349 string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"); 350 Assert.IsTrue(peer.Contains(Host)); 351 } 352 353 [Test] ServerCallContext_HostAndMethodPresent()354 public void ServerCallContext_HostAndMethodPresent() 355 { 356 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 357 { 358 Assert.IsTrue(context.Host.Contains(Host)); 359 Assert.AreEqual("/tests.Test/Unary", context.Method); 360 return Task.FromResult("PASS"); 361 }); 362 Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); 363 } 364 365 [Test] ServerCallContext_AuthContextNotPopulated()366 public void ServerCallContext_AuthContextNotPopulated() 367 { 368 helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => 369 { 370 Assert.IsFalse(context.AuthContext.IsPeerAuthenticated); 371 Assert.AreEqual(0, context.AuthContext.Properties.Count()); 372 return Task.FromResult("PASS"); 373 }); 374 Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); 375 } 376 } 377 } 378