ClientServerTest.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. #region Copyright notice and license
  2. // Copyright 2015 gRPC authors.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #endregion
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Linq;
  20. using System.Threading;
  21. using System.Threading.Tasks;
  22. using Grpc.Core;
  23. using Grpc.Core.Internal;
  24. using Grpc.Core.Profiling;
  25. using Grpc.Core.Utils;
  26. using NUnit.Framework;
  27. namespace Grpc.Core.Tests
  28. {
  29. public class ClientServerTest
  30. {
  31. const string Host = "127.0.0.1";
  32. MockServiceHelper helper;
  33. Server server;
  34. Channel channel;
  35. [SetUp]
  36. public void Init()
  37. {
  38. helper = new MockServiceHelper(Host);
  39. server = helper.GetServer();
  40. server.Start();
  41. channel = helper.GetChannel();
  42. }
  43. [TearDown]
  44. public void Cleanup()
  45. {
  46. channel.ShutdownAsync().Wait();
  47. server.ShutdownAsync().Wait();
  48. }
  49. [Test]
  50. public async Task UnaryCall()
  51. {
  52. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  53. {
  54. return Task.FromResult(request);
  55. });
  56. Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC"));
  57. Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC"));
  58. }
  59. [Test]
  60. public void UnaryCall_ServerHandlerThrows()
  61. {
  62. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  63. {
  64. throw new Exception("This was thrown on purpose by a test");
  65. });
  66. var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  67. Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
  68. var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
  69. Assert.AreEqual(StatusCode.Unknown, ex2.Status.StatusCode);
  70. }
  71. [Test]
  72. public void UnaryCall_ServerHandlerThrowsRpcException()
  73. {
  74. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  75. {
  76. throw new RpcException(new Status(StatusCode.Unauthenticated, ""));
  77. });
  78. var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  79. Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
  80. Assert.AreEqual(0, ex.Trailers.Count);
  81. var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
  82. Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
  83. Assert.AreEqual(0, ex.Trailers.Count);
  84. }
  85. [Test]
  86. public void UnaryCall_ServerHandlerThrowsRpcExceptionWithTrailers()
  87. {
  88. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  89. {
  90. var trailers = new Metadata { {"xyz", "xyz-value"} };
  91. throw new RpcException(new Status(StatusCode.Unauthenticated, ""), trailers);
  92. });
  93. var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  94. Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
  95. Assert.AreEqual(1, ex.Trailers.Count);
  96. Assert.AreEqual("xyz", ex.Trailers[0].Key);
  97. Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
  98. var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
  99. Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
  100. Assert.AreEqual(1, ex2.Trailers.Count);
  101. Assert.AreEqual("xyz", ex2.Trailers[0].Key);
  102. Assert.AreEqual("xyz-value", ex2.Trailers[0].Value);
  103. }
  104. [Test]
  105. public void UnaryCall_ServerHandlerSetsStatus()
  106. {
  107. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  108. {
  109. context.Status = new Status(StatusCode.Unauthenticated, "");
  110. return Task.FromResult("");
  111. });
  112. var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  113. Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
  114. Assert.AreEqual(0, ex.Trailers.Count);
  115. var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
  116. Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
  117. Assert.AreEqual(0, ex2.Trailers.Count);
  118. }
  119. [Test]
  120. public void UnaryCall_StatusDebugErrorStringNotTransmittedFromServer()
  121. {
  122. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  123. {
  124. context.Status = new Status(StatusCode.Unauthenticated, "", new CoreErrorDetailException("this DebugErrorString value should not be transmitted to the client"));
  125. return Task.FromResult("");
  126. });
  127. var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  128. Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
  129. StringAssert.Contains("Error received from peer", ex.Status.DebugException.Message, "Is \"Error received from peer\" still a valid substring to search for in the client-generated error message from C-core?");
  130. Assert.AreEqual(0, ex.Trailers.Count);
  131. var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
  132. Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
  133. StringAssert.Contains("Error received from peer", ex2.Status.DebugException.Message, "Is \"Error received from peer\" still a valid substring to search for in the client-generated error message from C-core?");
  134. Assert.AreEqual(0, ex2.Trailers.Count);
  135. }
  136. [Test]
  137. public void UnaryCall_ServerHandlerSetsStatusAndTrailers()
  138. {
  139. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  140. {
  141. context.Status = new Status(StatusCode.Unauthenticated, "");
  142. context.ResponseTrailers.Add("xyz", "xyz-value");
  143. return Task.FromResult("");
  144. });
  145. var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  146. Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
  147. Assert.AreEqual(1, ex.Trailers.Count);
  148. Assert.AreEqual("xyz", ex.Trailers[0].Key);
  149. Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
  150. var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
  151. Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
  152. Assert.AreEqual(1, ex2.Trailers.Count);
  153. Assert.AreEqual("xyz", ex2.Trailers[0].Key);
  154. Assert.AreEqual("xyz-value", ex2.Trailers[0].Value);
  155. }
  156. [Test]
  157. public async Task ClientStreamingCall()
  158. {
  159. helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
  160. {
  161. string result = "";
  162. await requestStream.ForEachAsync((request) =>
  163. {
  164. result += request;
  165. return TaskUtils.CompletedTask;
  166. });
  167. await Task.Delay(100);
  168. return result;
  169. });
  170. var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
  171. await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
  172. Assert.AreEqual("ABC", await call.ResponseAsync);
  173. Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
  174. Assert.IsNotNull(call.GetTrailers());
  175. }
  176. [Test]
  177. public async Task ServerStreamingCall()
  178. {
  179. helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) =>
  180. {
  181. await responseStream.WriteAllAsync(request.Split(new []{' '}));
  182. context.ResponseTrailers.Add("xyz", "");
  183. });
  184. var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "A B C");
  185. CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync());
  186. Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
  187. Assert.AreEqual("xyz", call.GetTrailers()[0].Key);
  188. }
  189. [Test]
  190. public async Task ServerStreamingCall_EndOfStreamIsIdempotent()
  191. {
  192. helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) => TaskUtils.CompletedTask);
  193. var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
  194. Assert.IsFalse(await call.ResponseStream.MoveNext());
  195. Assert.IsFalse(await call.ResponseStream.MoveNext());
  196. }
  197. [Test]
  198. public void ServerStreamingCall_ErrorCanBeAwaitedTwice()
  199. {
  200. helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) =>
  201. {
  202. context.Status = new Status(StatusCode.InvalidArgument, "");
  203. return TaskUtils.CompletedTask;
  204. });
  205. var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
  206. var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
  207. Assert.AreEqual(StatusCode.InvalidArgument, ex.Status.StatusCode);
  208. // attempting MoveNext again should result in throwing the same exception.
  209. var ex2 = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
  210. Assert.AreEqual(StatusCode.InvalidArgument, ex2.Status.StatusCode);
  211. }
  212. [Test]
  213. public void ServerStreamingCall_TrailersFromMultipleSourcesGetConcatenated()
  214. {
  215. helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) =>
  216. {
  217. context.ResponseTrailers.Add("xyz", "xyz-value");
  218. throw new RpcException(new Status(StatusCode.InvalidArgument, ""), new Metadata { {"abc", "abc-value"} });
  219. });
  220. var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
  221. var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
  222. Assert.AreEqual(StatusCode.InvalidArgument, ex.Status.StatusCode);
  223. Assert.AreEqual(2, call.GetTrailers().Count);
  224. Assert.AreEqual(2, ex.Trailers.Count);
  225. Assert.AreEqual("xyz", ex.Trailers[0].Key);
  226. Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
  227. Assert.AreEqual("abc", ex.Trailers[1].Key);
  228. Assert.AreEqual("abc-value", ex.Trailers[1].Value);
  229. }
  230. [Test]
  231. public async Task DuplexStreamingCall()
  232. {
  233. helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) =>
  234. {
  235. while (await requestStream.MoveNext())
  236. {
  237. await responseStream.WriteAsync(requestStream.Current);
  238. }
  239. context.ResponseTrailers.Add("xyz", "xyz-value");
  240. });
  241. var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall());
  242. await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
  243. CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync());
  244. Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
  245. Assert.AreEqual("xyz-value", call.GetTrailers()[0].Value);
  246. }
  247. [Test]
  248. public async Task AsyncUnaryCall_EchoMetadata()
  249. {
  250. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  251. {
  252. foreach (Metadata.Entry metadataEntry in context.RequestHeaders)
  253. {
  254. if (metadataEntry.Key != "user-agent")
  255. {
  256. context.ResponseTrailers.Add(metadataEntry);
  257. }
  258. }
  259. return Task.FromResult("");
  260. });
  261. var headers = new Metadata
  262. {
  263. { "ascii-header", "abcdefg" },
  264. { "binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff } }
  265. };
  266. var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC");
  267. await call;
  268. Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
  269. var trailers = call.GetTrailers();
  270. Assert.AreEqual(2, trailers.Count);
  271. Assert.AreEqual(headers[0].Key, trailers[0].Key);
  272. Assert.AreEqual(headers[0].Value, trailers[0].Value);
  273. Assert.AreEqual(headers[1].Key, trailers[1].Key);
  274. CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes);
  275. }
  276. [Test]
  277. public void UnknownMethodHandler()
  278. {
  279. var nonexistentMethod = new Method<string, string>(
  280. MethodType.Unary,
  281. MockServiceHelper.ServiceName,
  282. "NonExistentMethod",
  283. Marshallers.StringMarshaller,
  284. Marshallers.StringMarshaller);
  285. var callDetails = new CallInvocationDetails<string, string>(channel, nonexistentMethod, new CallOptions());
  286. var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(callDetails, "abc"));
  287. Assert.AreEqual(StatusCode.Unimplemented, ex.Status.StatusCode);
  288. }
  289. [Test]
  290. public void StatusDetailIsUtf8()
  291. {
  292. // some japanese and chinese characters
  293. var nonAsciiString = "\u30a1\u30a2\u30a3 \u62b5\u6297\u662f\u5f92\u52b3\u7684";
  294. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  295. {
  296. context.Status = new Status(StatusCode.Unknown, nonAsciiString);
  297. return Task.FromResult("");
  298. });
  299. var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  300. Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
  301. Assert.AreEqual(nonAsciiString, ex.Status.Detail);
  302. }
  303. [Test]
  304. public void ServerCallContext_PeerInfoPresent()
  305. {
  306. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  307. {
  308. return Task.FromResult(context.Peer);
  309. });
  310. string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc");
  311. Assert.IsTrue(peer.Contains(Host));
  312. }
  313. [Test]
  314. public void ServerCallContext_HostAndMethodPresent()
  315. {
  316. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  317. {
  318. Assert.IsTrue(context.Host.Contains(Host));
  319. Assert.AreEqual("/tests.Test/Unary", context.Method);
  320. return Task.FromResult("PASS");
  321. });
  322. Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  323. }
  324. [Test]
  325. public void ServerCallContext_AuthContextNotPopulated()
  326. {
  327. helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
  328. {
  329. Assert.IsFalse(context.AuthContext.IsPeerAuthenticated);
  330. Assert.AreEqual(0, context.AuthContext.Properties.Count());
  331. return Task.FromResult("PASS");
  332. });
  333. Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
  334. }
  335. }
  336. }