Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: third_party/grpc/src/csharp/Grpc.IntegrationTesting/InteropClient.cs

Issue 1932353002: Initial checkin of gRPC to third_party/ Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #region Copyright notice and license
2
3 // Copyright 2015, Google Inc.
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 // * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 // * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 #endregion
33
34 using System;
35 using System.Collections.Generic;
36 using System.IO;
37 using System.Linq;
38 using System.Text.RegularExpressions;
39 using System.Threading;
40 using System.Threading.Tasks;
41
42 using CommandLine;
43 using CommandLine.Text;
44 using Google.Apis.Auth.OAuth2;
45 using Google.Protobuf;
46 using Grpc.Auth;
47 using Grpc.Core;
48 using Grpc.Core.Utils;
49 using Grpc.Testing;
50 using Newtonsoft.Json.Linq;
51 using NUnit.Framework;
52
53 namespace Grpc.IntegrationTesting
54 {
55 public class InteropClient
56 {
57 private class ClientOptions
58 {
59 [Option("server_host", DefaultValue = "127.0.0.1")]
60 public string ServerHost { get; set; }
61
62 [Option("server_host_override", DefaultValue = TestCredentials.Defau ltHostOverride)]
63 public string ServerHostOverride { get; set; }
64
65 [Option("server_port", Required = true)]
66 public int ServerPort { get; set; }
67
68 [Option("test_case", DefaultValue = "large_unary")]
69 public string TestCase { get; set; }
70
71 // Deliberately using nullable bool type to allow --use_tls=true syn tax (as opposed to --use_tls)
72 [Option("use_tls", DefaultValue = false)]
73 public bool? UseTls { get; set; }
74
75 // Deliberately using nullable bool type to allow --use_test_ca=true syntax (as opposed to --use_test_ca)
76 [Option("use_test_ca", DefaultValue = false)]
77 public bool? UseTestCa { get; set; }
78
79 [Option("default_service_account", Required = false)]
80 public string DefaultServiceAccount { get; set; }
81
82 [Option("oauth_scope", Required = false)]
83 public string OAuthScope { get; set; }
84
85 [Option("service_account_key_file", Required = false)]
86 public string ServiceAccountKeyFile { get; set; }
87
88 [HelpOption]
89 public string GetUsage()
90 {
91 var help = new HelpText
92 {
93 Heading = "gRPC C# interop testing client",
94 AddDashesToOption = true
95 };
96 help.AddPreOptionsLine("Usage:");
97 help.AddOptions(this);
98 return help;
99 }
100 }
101
102 ClientOptions options;
103
104 private InteropClient(ClientOptions options)
105 {
106 this.options = options;
107 }
108
109 public static void Run(string[] args)
110 {
111 var options = new ClientOptions();
112 if (!Parser.Default.ParseArguments(args, options))
113 {
114 Environment.Exit(1);
115 }
116
117 var interopClient = new InteropClient(options);
118 interopClient.Run().Wait();
119 }
120
121 private async Task Run()
122 {
123 var credentials = await CreateCredentialsAsync();
124
125 List<ChannelOption> channelOptions = null;
126 if (!string.IsNullOrEmpty(options.ServerHostOverride))
127 {
128 channelOptions = new List<ChannelOption>
129 {
130 new ChannelOption(ChannelOptions.SslTargetNameOverride, opti ons.ServerHostOverride)
131 };
132 }
133 var channel = new Channel(options.ServerHost, options.ServerPort, cr edentials, channelOptions);
134 await RunTestCaseAsync(channel, options);
135 await channel.ShutdownAsync();
136 }
137
138 private async Task<ChannelCredentials> CreateCredentialsAsync()
139 {
140 var credentials = ChannelCredentials.Insecure;
141 if (options.UseTls.Value)
142 {
143 credentials = options.UseTestCa.Value ? TestCredentials.CreateSs lCredentials() : new SslCredentials();
144 }
145
146 if (options.TestCase == "jwt_token_creds")
147 {
148 var googleCredential = await GoogleCredential.GetApplicationDefa ultAsync();
149 Assert.IsTrue(googleCredential.IsCreateScopedRequired);
150 credentials = ChannelCredentials.Create(credentials, googleCrede ntial.ToCallCredentials());
151 }
152
153 if (options.TestCase == "compute_engine_creds")
154 {
155 var googleCredential = await GoogleCredential.GetApplicationDefa ultAsync();
156 Assert.IsFalse(googleCredential.IsCreateScopedRequired);
157 credentials = ChannelCredentials.Create(credentials, googleCrede ntial.ToCallCredentials());
158 }
159 return credentials;
160 }
161
162 private async Task RunTestCaseAsync(Channel channel, ClientOptions optio ns)
163 {
164 var client = new TestService.TestServiceClient(channel);
165 switch (options.TestCase)
166 {
167 case "empty_unary":
168 RunEmptyUnary(client);
169 break;
170 case "large_unary":
171 RunLargeUnary(client);
172 break;
173 case "client_streaming":
174 await RunClientStreamingAsync(client);
175 break;
176 case "server_streaming":
177 await RunServerStreamingAsync(client);
178 break;
179 case "ping_pong":
180 await RunPingPongAsync(client);
181 break;
182 case "empty_stream":
183 await RunEmptyStreamAsync(client);
184 break;
185 case "compute_engine_creds":
186 RunComputeEngineCreds(client, options.DefaultServiceAccount, options.OAuthScope);
187 break;
188 case "jwt_token_creds":
189 RunJwtTokenCreds(client);
190 break;
191 case "oauth2_auth_token":
192 await RunOAuth2AuthTokenAsync(client, options.OAuthScope);
193 break;
194 case "per_rpc_creds":
195 await RunPerRpcCredsAsync(client, options.OAuthScope);
196 break;
197 case "cancel_after_begin":
198 await RunCancelAfterBeginAsync(client);
199 break;
200 case "cancel_after_first_response":
201 await RunCancelAfterFirstResponseAsync(client);
202 break;
203 case "timeout_on_sleeping_server":
204 await RunTimeoutOnSleepingServerAsync(client);
205 break;
206 case "custom_metadata":
207 await RunCustomMetadataAsync(client);
208 break;
209 case "status_code_and_message":
210 await RunStatusCodeAndMessageAsync(client);
211 break;
212 case "unimplemented_method":
213 RunUnimplementedMethod(new UnimplementedService.Unimplemente dServiceClient(channel));
214 break;
215 default:
216 throw new ArgumentException("Unknown test case " + options.T estCase);
217 }
218 }
219
220 public static void RunEmptyUnary(TestService.ITestServiceClient client)
221 {
222 Console.WriteLine("running empty_unary");
223 var response = client.EmptyCall(new Empty());
224 Assert.IsNotNull(response);
225 Console.WriteLine("Passed!");
226 }
227
228 public static void RunLargeUnary(TestService.ITestServiceClient client)
229 {
230 Console.WriteLine("running large_unary");
231 var request = new SimpleRequest
232 {
233 ResponseType = PayloadType.COMPRESSABLE,
234 ResponseSize = 314159,
235 Payload = CreateZerosPayload(271828)
236 };
237 var response = client.UnaryCall(request);
238
239 Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
240 Assert.AreEqual(314159, response.Payload.Body.Length);
241 Console.WriteLine("Passed!");
242 }
243
244 public static async Task RunClientStreamingAsync(TestService.ITestServic eClient client)
245 {
246 Console.WriteLine("running client_streaming");
247
248 var bodySizes = new List<int> { 27182, 8, 1828, 45904 }.ConvertAll(( size) => new StreamingInputCallRequest { Payload = CreateZerosPayload(size) });
249
250 using (var call = client.StreamingInputCall())
251 {
252 await call.RequestStream.WriteAllAsync(bodySizes);
253
254 var response = await call.ResponseAsync;
255 Assert.AreEqual(74922, response.AggregatedPayloadSize);
256 }
257 Console.WriteLine("Passed!");
258 }
259
260 public static async Task RunServerStreamingAsync(TestService.ITestServic eClient client)
261 {
262 Console.WriteLine("running server_streaming");
263
264 var bodySizes = new List<int> { 31415, 9, 2653, 58979 };
265
266 var request = new StreamingOutputCallRequest
267 {
268 ResponseType = PayloadType.COMPRESSABLE,
269 ResponseParameters = { bodySizes.ConvertAll((size) => new Respon seParameters { Size = size }) }
270 };
271
272 using (var call = client.StreamingOutputCall(request))
273 {
274 var responseList = await call.ResponseStream.ToListAsync();
275 foreach (var res in responseList)
276 {
277 Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type);
278 }
279 CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((it em) => item.Payload.Body.Length));
280 }
281 Console.WriteLine("Passed!");
282 }
283
284 public static async Task RunPingPongAsync(TestService.ITestServiceClient client)
285 {
286 Console.WriteLine("running ping_pong");
287
288 using (var call = client.FullDuplexCall())
289 {
290 await call.RequestStream.WriteAsync(new StreamingOutputCallReque st
291 {
292 ResponseType = PayloadType.COMPRESSABLE,
293 ResponseParameters = { new ResponseParameters { Size = 31415 } },
294 Payload = CreateZerosPayload(27182)
295 });
296
297 Assert.IsTrue(await call.ResponseStream.MoveNext());
298 Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Cu rrent.Payload.Type);
299 Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body. Length);
300
301 await call.RequestStream.WriteAsync(new StreamingOutputCallReque st
302 {
303 ResponseType = PayloadType.COMPRESSABLE,
304 ResponseParameters = { new ResponseParameters { Size = 9 } } ,
305 Payload = CreateZerosPayload(8)
306 });
307
308 Assert.IsTrue(await call.ResponseStream.MoveNext());
309 Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Cu rrent.Payload.Type);
310 Assert.AreEqual(9, call.ResponseStream.Current.Payload.Body.Leng th);
311
312 await call.RequestStream.WriteAsync(new StreamingOutputCallReque st
313 {
314 ResponseType = PayloadType.COMPRESSABLE,
315 ResponseParameters = { new ResponseParameters { Size = 2653 } },
316 Payload = CreateZerosPayload(1828)
317 });
318
319 Assert.IsTrue(await call.ResponseStream.MoveNext());
320 Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Cu rrent.Payload.Type);
321 Assert.AreEqual(2653, call.ResponseStream.Current.Payload.Body.L ength);
322
323 await call.RequestStream.WriteAsync(new StreamingOutputCallReque st
324 {
325 ResponseType = PayloadType.COMPRESSABLE,
326 ResponseParameters = { new ResponseParameters { Size = 58979 } },
327 Payload = CreateZerosPayload(45904)
328 });
329
330 Assert.IsTrue(await call.ResponseStream.MoveNext());
331 Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Cu rrent.Payload.Type);
332 Assert.AreEqual(58979, call.ResponseStream.Current.Payload.Body. Length);
333
334 await call.RequestStream.CompleteAsync();
335
336 Assert.IsFalse(await call.ResponseStream.MoveNext());
337 }
338 Console.WriteLine("Passed!");
339 }
340
341 public static async Task RunEmptyStreamAsync(TestService.ITestServiceCli ent client)
342 {
343 Console.WriteLine("running empty_stream");
344 using (var call = client.FullDuplexCall())
345 {
346 await call.RequestStream.CompleteAsync();
347
348 var responseList = await call.ResponseStream.ToListAsync();
349 Assert.AreEqual(0, responseList.Count);
350 }
351 Console.WriteLine("Passed!");
352 }
353
354 public static void RunComputeEngineCreds(TestService.TestServiceClient c lient, string defaultServiceAccount, string oauthScope)
355 {
356 Console.WriteLine("running compute_engine_creds");
357
358 var request = new SimpleRequest
359 {
360 ResponseType = PayloadType.COMPRESSABLE,
361 ResponseSize = 314159,
362 Payload = CreateZerosPayload(271828),
363 FillUsername = true,
364 FillOauthScope = true
365 };
366
367 // not setting credentials here because they were set on channel alr eady
368 var response = client.UnaryCall(request);
369
370 Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
371 Assert.AreEqual(314159, response.Payload.Body.Length);
372 Assert.False(string.IsNullOrEmpty(response.OauthScope));
373 Assert.True(oauthScope.Contains(response.OauthScope));
374 Assert.AreEqual(defaultServiceAccount, response.Username);
375 Console.WriteLine("Passed!");
376 }
377
378 public static void RunJwtTokenCreds(TestService.TestServiceClient client )
379 {
380 Console.WriteLine("running jwt_token_creds");
381
382 var request = new SimpleRequest
383 {
384 ResponseType = PayloadType.COMPRESSABLE,
385 ResponseSize = 314159,
386 Payload = CreateZerosPayload(271828),
387 FillUsername = true,
388 };
389
390 // not setting credentials here because they were set on channel alr eady
391 var response = client.UnaryCall(request);
392
393 Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
394 Assert.AreEqual(314159, response.Payload.Body.Length);
395 Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username) ;
396 Console.WriteLine("Passed!");
397 }
398
399 public static async Task RunOAuth2AuthTokenAsync(TestService.TestService Client client, string oauthScope)
400 {
401 Console.WriteLine("running oauth2_auth_token");
402 ITokenAccess credential = (await GoogleCredential.GetApplicationDefa ultAsync()).CreateScoped(new[] { oauthScope });
403 string oauth2Token = await credential.GetAccessTokenForRequestAsync( );
404
405 var credentials = GoogleGrpcCredentials.FromAccessToken(oauth2Token) ;
406 var request = new SimpleRequest
407 {
408 FillUsername = true,
409 FillOauthScope = true
410 };
411
412 var response = client.UnaryCall(request, new CallOptions(credentials : credentials));
413
414 Assert.False(string.IsNullOrEmpty(response.OauthScope));
415 Assert.True(oauthScope.Contains(response.OauthScope));
416 Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username) ;
417 Console.WriteLine("Passed!");
418 }
419
420 public static async Task RunPerRpcCredsAsync(TestService.TestServiceClie nt client, string oauthScope)
421 {
422 Console.WriteLine("running per_rpc_creds");
423 ITokenAccess googleCredential = await GoogleCredential.GetApplicatio nDefaultAsync();
424
425 var credentials = googleCredential.ToCallCredentials();
426 var request = new SimpleRequest
427 {
428 FillUsername = true,
429 };
430
431 var response = client.UnaryCall(request, new CallOptions(credentials : credentials));
432
433 Assert.AreEqual(GetEmailFromServiceAccountFile(), response.Username) ;
434 Console.WriteLine("Passed!");
435 }
436
437 public static async Task RunCancelAfterBeginAsync(TestService.ITestServi ceClient client)
438 {
439 Console.WriteLine("running cancel_after_begin");
440
441 var cts = new CancellationTokenSource();
442 using (var call = client.StreamingInputCall(cancellationToken: cts.T oken))
443 {
444 // TODO(jtattermusch): we need this to ensure call has been init iated once we cancel it.
445 await Task.Delay(1000);
446 cts.Cancel();
447
448 var ex = Assert.Throws<RpcException>(async () => await call.Resp onseAsync);
449 Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
450 }
451 Console.WriteLine("Passed!");
452 }
453
454 public static async Task RunCancelAfterFirstResponseAsync(TestService.IT estServiceClient client)
455 {
456 Console.WriteLine("running cancel_after_first_response");
457
458 var cts = new CancellationTokenSource();
459 using (var call = client.FullDuplexCall(cancellationToken: cts.Token ))
460 {
461 await call.RequestStream.WriteAsync(new StreamingOutputCallReque st
462 {
463 ResponseType = PayloadType.COMPRESSABLE,
464 ResponseParameters = { new ResponseParameters { Size = 31415 } },
465 Payload = CreateZerosPayload(27182)
466 });
467
468 Assert.IsTrue(await call.ResponseStream.MoveNext());
469 Assert.AreEqual(PayloadType.COMPRESSABLE, call.ResponseStream.Cu rrent.Payload.Type);
470 Assert.AreEqual(31415, call.ResponseStream.Current.Payload.Body. Length);
471
472 cts.Cancel();
473
474 var ex = Assert.Throws<RpcException>(async () => await call.Resp onseStream.MoveNext());
475 Assert.AreEqual(StatusCode.Cancelled, ex.Status.StatusCode);
476 }
477 Console.WriteLine("Passed!");
478 }
479
480 public static async Task RunTimeoutOnSleepingServerAsync(TestService.ITe stServiceClient client)
481 {
482 Console.WriteLine("running timeout_on_sleeping_server");
483
484 var deadline = DateTime.UtcNow.AddMilliseconds(1);
485 using (var call = client.FullDuplexCall(deadline: deadline))
486 {
487 try
488 {
489 await call.RequestStream.WriteAsync(new StreamingOutputCallR equest { Payload = CreateZerosPayload(27182) });
490 }
491 catch (InvalidOperationException)
492 {
493 // Deadline was reached before write has started. Eat the ex ception and continue.
494 }
495
496 var ex = Assert.Throws<RpcException>(async () => await call.Resp onseStream.MoveNext());
497 Assert.AreEqual(StatusCode.DeadlineExceeded, ex.Status.StatusCod e);
498 }
499 Console.WriteLine("Passed!");
500 }
501
502 public static async Task RunCustomMetadataAsync(TestService.ITestService Client client)
503 {
504 Console.WriteLine("running custom_metadata");
505 {
506 // step 1: test unary call
507 var request = new SimpleRequest
508 {
509 ResponseType = PayloadType.COMPRESSABLE,
510 ResponseSize = 314159,
511 Payload = CreateZerosPayload(271828)
512 };
513
514 var call = client.UnaryCallAsync(request, headers: CreateTestMet adata());
515 await call.ResponseAsync;
516
517 var responseHeaders = await call.ResponseHeadersAsync;
518 var responseTrailers = call.GetTrailers();
519
520 Assert.AreEqual("test_initial_metadata_value", responseHeaders.F irst((entry) => entry.Key == "x-grpc-test-echo-initial").Value);
521 CollectionAssert.AreEqual(new byte[] { 0xab, 0xab, 0xab }, respo nseTrailers.First((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").Value Bytes);
522 }
523
524 {
525 // step 2: test full duplex call
526 var request = new StreamingOutputCallRequest
527 {
528 ResponseType = PayloadType.COMPRESSABLE,
529 ResponseParameters = { new ResponseParameters { Size = 31415 } },
530 Payload = CreateZerosPayload(27182)
531 };
532
533 var call = client.FullDuplexCall(headers: CreateTestMetadata());
534 var responseHeaders = await call.ResponseHeadersAsync;
535
536 await call.RequestStream.WriteAsync(request);
537 await call.RequestStream.CompleteAsync();
538 await call.ResponseStream.ToListAsync();
539
540 var responseTrailers = call.GetTrailers();
541
542 Assert.AreEqual("test_initial_metadata_value", responseHeaders.F irst((entry) => entry.Key == "x-grpc-test-echo-initial").Value);
543 CollectionAssert.AreEqual(new byte[] { 0xab, 0xab, 0xab }, respo nseTrailers.First((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").Value Bytes);
544 }
545
546 Console.WriteLine("Passed!");
547 }
548
549 public static async Task RunStatusCodeAndMessageAsync(TestService.ITestS erviceClient client)
550 {
551 Console.WriteLine("running status_code_and_message");
552 var echoStatus = new EchoStatus
553 {
554 Code = 2,
555 Message = "test status message"
556 };
557
558 {
559 // step 1: test unary call
560 var request = new SimpleRequest { ResponseStatus = echoStatus };
561
562 var e = Assert.Throws<RpcException>(() => client.UnaryCall(reque st));
563 Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
564 Assert.AreEqual(echoStatus.Message, e.Status.Detail);
565 }
566
567 {
568 // step 2: test full duplex call
569 var request = new StreamingOutputCallRequest { ResponseStatus = echoStatus };
570
571 var call = client.FullDuplexCall();
572 await call.RequestStream.WriteAsync(request);
573 await call.RequestStream.CompleteAsync();
574
575 var e = Assert.Throws<RpcException>(async () => await call.Respo nseStream.ToListAsync());
576 Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
577 Assert.AreEqual(echoStatus.Message, e.Status.Detail);
578 }
579
580 Console.WriteLine("Passed!");
581 }
582
583 public static void RunUnimplementedMethod(UnimplementedService.IUnimplem entedServiceClient client)
584 {
585 Console.WriteLine("running unimplemented_method");
586 var e = Assert.Throws<RpcException>(() => client.UnimplementedCall(n ew Empty()));
587
588 Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode);
589 Assert.AreEqual("", e.Status.Detail);
590 Console.WriteLine("Passed!");
591 }
592
593 private static Payload CreateZerosPayload(int size)
594 {
595 return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
596 }
597
598 // extracts the client_email field from service account file used for au th test cases
599 private static string GetEmailFromServiceAccountFile()
600 {
601 string keyFile = Environment.GetEnvironmentVariable("GOOGLE_APPLICAT ION_CREDENTIALS");
602 Assert.IsNotNull(keyFile);
603
604 var jobject = JObject.Parse(File.ReadAllText(keyFile));
605 string email = jobject.GetValue("client_email").Value<string>();
606 Assert.IsTrue(email.Length > 0); // spec requires nonempty client e mail.
607 return email;
608 }
609
610 private static Metadata CreateTestMetadata()
611 {
612 return new Metadata
613 {
614 {"x-grpc-test-echo-initial", "test_initial_metadata_value"},
615 {"x-grpc-test-echo-trailing-bin", new byte[] {0xab, 0xab, 0xab}}
616 };
617 }
618 }
619 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698