xref: /aosp_15_r20/external/grpc-grpc/src/objective-c/tests/CppCronetTests/CppCronetEnd2EndTests.mm (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1/*
2 *
3 * Copyright 2019 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 */
18
19#import <Cronet/Cronet.h>
20#import <XCTest/XCTest.h>
21
22#import <grpc/grpc_cronet.h>
23#import <grpcpp/client_context.h>
24#import <grpcpp/create_channel.h>
25#import <grpcpp/resource_quota.h>
26#import <grpcpp/security/cronet_credentials.h>
27#import <grpcpp/server_builder.h>
28#import <grpcpp/server_context.h>
29#import <grpcpp/support/client_interceptor.h>
30#import <grpcpp/support/config.h>
31#import "src/proto/grpc/testing/echo.grpc.pb.h"
32
33#import "TestHelper.h"
34#import "test/core/end2end/data/ssl_test_data.h"
35
36#import <map>
37#import <sstream>
38#import <thread>
39#import <vector>
40
41using namespace grpc::testing;
42using grpc::ClientContext;
43using grpc::ServerContext;
44using grpc::Status;
45using std::chrono::system_clock;
46
47@interface CppCronetEnd2EndTests : XCTestCase
48
49@end
50
51@implementation CppCronetEnd2EndTests {
52  std::unique_ptr<grpc::Server> _server;
53  TestServiceImpl _service;
54  TestServiceImpl _foo_service;
55}
56
57// The setUp() function is run before the test cases run and only run once
58+ (void)setUp {
59  [super setUp];
60  configureCronet();
61}
62
63- (void)startServer {
64  if (_server) {
65    // server is already running
66    return;
67  }
68
69  grpc::ServerBuilder builder;
70  grpc::SslServerCredentialsOptions ssl_opts;
71
72  ssl_opts.pem_root_certs = "";
73  grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key, test_server1_cert};
74  ssl_opts.pem_key_cert_pairs.push_back(pkcp);
75  auto server_creds = SslServerCredentials(ssl_opts);
76  builder.AddListeningPort("localhost:5000", server_creds);
77  builder.RegisterService(&_service);
78  builder.RegisterService("foo.test.youtube.com", &_foo_service);
79  _server = builder.BuildAndStart();
80}
81
82- (void)stopServer {
83  _server.reset();
84}
85
86- (void)restartServer {
87  [self stopServer];
88  [self startServer];
89}
90
91- (void)setUp {
92  [self startServer];
93}
94
95- (void)sendRPCWithStub:(EchoTestService::Stub*)stub
96                numRPCs:(int)num_rpcs
97     withBinaryMetadata:(BOOL)with_binary_metadata {
98  EchoRequest request;
99  EchoResponse response;
100  request.set_message("Hello hello hello hello");
101
102  for (int i = 0; i < num_rpcs; ++i) {
103    ClientContext context;
104    if (with_binary_metadata) {
105      char bytes[8] = {'\0', '\1', '\2', '\3', '\4', '\5', '\6', static_cast<char>(i)};
106      context.AddMetadata("custom-bin", std::string(bytes, 8));
107    }
108    context.set_compression_algorithm(GRPC_COMPRESS_GZIP);
109    Status s = stub->Echo(&context, request, &response);
110    XCTAssertEqual(response.message(), request.message());
111    XCTAssertTrue(s.ok());
112  }
113}
114
115- (std::shared_ptr<::grpc::Channel>)getChannel {
116  stream_engine* cronetEngine = [Cronet getGlobalEngine];
117  auto cronetChannelCredentials = grpc::CronetChannelCredentials(cronetEngine);
118  grpc::ChannelArguments args;
119  args.SetSslTargetNameOverride("foo.test.google.fr");
120  args.SetUserAgentPrefix("custom_prefix");
121  args.SetString(GRPC_ARG_SECONDARY_USER_AGENT_STRING, "end2end_test");
122  auto channel = grpc::CreateCustomChannel("127.0.0.1:5000", cronetChannelCredentials, args);
123  return channel;
124}
125
126- (std::shared_ptr<::grpc::Channel>)getChannelWithInterceptors:
127    (std::vector<std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>>)creators {
128  stream_engine* cronetEngine = [Cronet getGlobalEngine];
129  auto cronetChannelCredentials = grpc::CronetChannelCredentials(cronetEngine);
130  grpc::ChannelArguments args;
131  args.SetSslTargetNameOverride("foo.test.google.fr");
132  args.SetUserAgentPrefix("custom_prefix");
133  args.SetString(GRPC_ARG_SECONDARY_USER_AGENT_STRING, "end2end_test");
134  auto channel = grpc::experimental::CreateCustomChannelWithInterceptors(
135      "127.0.01:5000", cronetChannelCredentials, args, std::move(creators));
136  return channel;
137}
138
139- (std::unique_ptr<EchoTestService::Stub>)getStub {
140  auto channel = [self getChannel];
141  auto stub = EchoTestService::NewStub(channel);
142  return stub;
143}
144
145- (void)testUserAgent {
146  ClientContext context;
147  EchoRequest request;
148  EchoResponse response;
149  request.set_message("Hello");
150  request.mutable_param()->set_echo_metadata(true);
151  auto stub = [self getStub];
152  Status s = stub->Echo(&context, request, &response);
153  XCTAssertTrue(s.ok());
154  const auto& trailing_metadata = context.GetServerTrailingMetadata();
155  auto iter = trailing_metadata.find("user-agent");
156  XCTAssert(iter->second.starts_with("custom_prefix grpc-c++"));
157}
158
159- (void)testMultipleRPCs {
160  auto stub = [self getStub];
161  std::vector<std::thread> threads;
162  threads.reserve(10);
163  for (int i = 0; i < 10; ++i) {
164    threads.emplace_back(
165        [self, &stub]() { [self sendRPCWithStub:stub.get() numRPCs:10 withBinaryMetadata:NO]; });
166  }
167  for (int i = 0; i < 10; ++i) {
168    threads[i].join();
169  }
170}
171
172- (void)testMultipleRPCsWithBinaryMetadata {
173  auto stub = [self getStub];
174  std::vector<std::thread> threads;
175  threads.reserve(10);
176  for (int i = 0; i < 10; ++i) {
177    threads.emplace_back(
178        [self, &stub]() { [self sendRPCWithStub:stub.get() numRPCs:10 withBinaryMetadata:YES]; });
179  }
180  for (int i = 0; i < 10; ++i) {
181    threads[i].join();
182  }
183}
184
185- (void)testEmptyBinaryMetadata {
186  EchoRequest request;
187  EchoResponse response;
188  request.set_message("Hello hello hello hello");
189  ClientContext context;
190  context.AddMetadata("custom-bin", "");
191  auto stub = [self getStub];
192  Status s = stub->Echo(&context, request, &response);
193  XCTAssertEqual(response.message(), request.message());
194  XCTAssertTrue(s.ok());
195}
196
197- (void)testReconnectChannel {
198  auto stub = [self getStub];
199  [self sendRPCWithStub:stub.get() numRPCs:1 withBinaryMetadata:NO];
200
201  [self restartServer];
202  [self sendRPCWithStub:stub.get() numRPCs:1 withBinaryMetadata:NO];
203}
204
205- (void)testRequestStreamOneRequest {
206  auto stub = [self getStub];
207  EchoRequest request;
208  EchoResponse response;
209  ClientContext context;
210  auto stream = stub->RequestStream(&context, &response);
211  request.set_message("hello");
212  XCTAssertTrue(stream->Write(request));
213  stream->WritesDone();
214  Status s = stream->Finish();
215  XCTAssertEqual(response.message(), request.message());
216  XCTAssertTrue(s.ok());
217  XCTAssertTrue(context.debug_error_string().empty());
218}
219
220- (void)testRequestStreamOneRequestWithCoalescingApi {
221  auto stub = [self getStub];
222  EchoRequest request;
223  EchoResponse response;
224  ClientContext context;
225  context.set_initial_metadata_corked(true);
226  auto stream = stub->RequestStream(&context, &response);
227  request.set_message("hello");
228  XCTAssertTrue(stream->Write(request));
229  stream->WritesDone();
230  Status s = stream->Finish();
231  XCTAssertEqual(response.message(), request.message());
232  XCTAssertTrue(s.ok());
233}
234
235- (void)testRequestStreamTwoRequests {
236  auto stub = [self getStub];
237  EchoRequest request;
238  EchoResponse response;
239  ClientContext context;
240  auto stream = stub->RequestStream(&context, &response);
241  request.set_message("hello");
242  XCTAssertTrue(stream->Write(request));
243  XCTAssertTrue(stream->Write(request));
244  stream->WritesDone();
245  Status s = stream->Finish();
246  XCTAssertEqual(response.message(), "hellohello");
247  XCTAssertTrue(s.ok());
248}
249
250- (void)testResponseStream {
251  auto stub = [self getStub];
252  EchoRequest request;
253  EchoResponse response;
254  ClientContext context;
255  request.set_message("hello");
256
257  auto stream = stub->ResponseStream(&context, request);
258  for (int i = 0; i < kServerDefaultResponseStreamsToSend; ++i) {
259    XCTAssertTrue(stream->Read(&response));
260    XCTAssertEqual(response.message(), request.message() + std::to_string(i));
261  }
262  XCTAssertFalse(stream->Read(&response));
263
264  Status s = stream->Finish();
265  XCTAssertTrue(s.ok());
266}
267
268- (void)testBidiStream {
269  auto stub = [self getStub];
270  EchoRequest request;
271  EchoResponse response;
272  ClientContext context;
273  std::string msg("hello");
274
275  auto stream = stub->BidiStream(&context);
276
277  for (int i = 0; i < kServerDefaultResponseStreamsToSend; ++i) {
278    request.set_message(msg + std::to_string(i));
279    XCTAssertTrue(stream->Write(request));
280    XCTAssertTrue(stream->Read(&response));
281    XCTAssertEqual(response.message(), request.message());
282  }
283
284  stream->WritesDone();
285  XCTAssertFalse(stream->Read(&response));
286  XCTAssertFalse(stream->Read(&response));
287
288  Status s = stream->Finish();
289  XCTAssertTrue(s.ok());
290}
291
292- (void)testBidiStreamWithCoalescingApi {
293  auto stub = [self getStub];
294  EchoRequest request;
295  EchoResponse response;
296  ClientContext context;
297  context.AddMetadata(kServerFinishAfterNReads, "3");
298  context.set_initial_metadata_corked(true);
299  std::string msg("hello");
300
301  auto stream = stub->BidiStream(&context);
302
303  request.set_message(msg + "0");
304  XCTAssertTrue(stream->Write(request));
305  XCTAssertTrue(stream->Read(&response));
306  XCTAssertEqual(response.message(), request.message());
307
308  request.set_message(msg + "1");
309  XCTAssertTrue(stream->Write(request));
310  XCTAssertTrue(stream->Read(&response));
311  XCTAssertEqual(response.message(), request.message());
312
313  request.set_message(msg + "2");
314  stream->WriteLast(request, grpc::WriteOptions());
315  XCTAssertTrue(stream->Read(&response));
316  XCTAssertEqual(response.message(), request.message());
317
318  XCTAssertFalse(stream->Read(&response));
319  XCTAssertFalse(stream->Read(&response));
320
321  Status s = stream->Finish();
322  XCTAssertTrue(s.ok());
323}
324
325- (void)testCancelBeforeStart {
326  auto stub = [self getStub];
327  EchoRequest request;
328  EchoResponse response;
329  ClientContext context;
330  request.set_message("hello");
331  context.TryCancel();
332  Status s = stub->Echo(&context, request, &response);
333  XCTAssertEqual("", response.message());
334  XCTAssertEqual(grpc::StatusCode::CANCELLED, s.error_code());
335}
336
337- (void)testClientCancelsRequestStream {
338  auto stub = [self getStub];
339  EchoRequest request;
340  EchoResponse response;
341  ClientContext context;
342  request.set_message("hello");
343
344  auto stream = stub->RequestStream(&context, &response);
345  XCTAssertTrue(stream->Write(request));
346  XCTAssertTrue(stream->Write(request));
347
348  context.TryCancel();
349
350  Status s = stream->Finish();
351  XCTAssertEqual(grpc::StatusCode::CANCELLED, s.error_code());
352  XCTAssertEqual(response.message(), "");
353}
354
355- (void)testClientCancelsResponseStream {
356  auto stub = [self getStub];
357  EchoRequest request;
358  EchoResponse response;
359  ClientContext context;
360  request.set_message("hello");
361
362  auto stream = stub->ResponseStream(&context, request);
363
364  XCTAssertTrue(stream->Read(&response));
365  XCTAssertEqual(response.message(), request.message() + "0");
366  XCTAssertTrue(stream->Read(&response));
367  XCTAssertEqual(response.message(), request.message() + "1");
368
369  context.TryCancel();
370
371  // The cancellation races with responses, so there might be zero or
372  // one responses pending, read till failure
373
374  if (stream->Read(&response)) {
375    XCTAssertEqual(response.message(), request.message() + "2");
376    // Since we have cancelled, we expect the next attempt to read to fail
377    XCTAssertFalse(stream->Read(&response));
378  }
379}
380
381- (void)testlClientCancelsBidiStream {
382  auto stub = [self getStub];
383  EchoRequest request;
384  EchoResponse response;
385  ClientContext context;
386  std::string msg("hello");
387
388  auto stream = stub->BidiStream(&context);
389
390  request.set_message(msg + "0");
391  XCTAssertTrue(stream->Write(request));
392  XCTAssertTrue(stream->Read(&response));
393  XCTAssertEqual(response.message(), request.message());
394
395  request.set_message(msg + "1");
396  XCTAssertTrue(stream->Write(request));
397
398  context.TryCancel();
399
400  // The cancellation races with responses, so there might be zero or
401  // one responses pending, read till failure
402
403  if (stream->Read(&response)) {
404    XCTAssertEqual(response.message(), request.message());
405    // Since we have cancelled, we expect the next attempt to read to fail
406    XCTAssertFalse(stream->Read(&response));
407  }
408
409  Status s = stream->Finish();
410  XCTAssertEqual(grpc::StatusCode::CANCELLED, s.error_code());
411}
412
413- (void)testNonExistingService {
414  auto channel = [self getChannel];
415  auto stub = grpc::testing::UnimplementedEchoService::NewStub(channel);
416
417  EchoRequest request;
418  EchoResponse response;
419  request.set_message("Hello");
420
421  ClientContext context;
422  Status s = stub->Unimplemented(&context, request, &response);
423  XCTAssertEqual(grpc::StatusCode::UNIMPLEMENTED, s.error_code());
424  XCTAssertEqual("", s.error_message());
425}
426
427- (void)testBinaryTrailer {
428  auto stub = [self getStub];
429  EchoRequest request;
430  EchoResponse response;
431  ClientContext context;
432
433  request.mutable_param()->set_echo_metadata(true);
434  DebugInfo* info = request.mutable_param()->mutable_debug_info();
435  info->add_stack_entries("stack_entry_1");
436  info->add_stack_entries("stack_entry_2");
437  info->add_stack_entries("stack_entry_3");
438  info->set_detail("detailed debug info");
439  std::string expected_string = info->SerializeAsString();
440  request.set_message("Hello");
441
442  Status s = stub->Echo(&context, request, &response);
443  XCTAssertFalse(s.ok());
444  auto trailers = context.GetServerTrailingMetadata();
445  XCTAssertEqual(1u, trailers.count(kDebugInfoTrailerKey));
446  auto iter = trailers.find(kDebugInfoTrailerKey);
447  XCTAssertEqual(expected_string, iter->second);
448  // Parse the returned trailer into a DebugInfo proto.
449  DebugInfo returned_info;
450  XCTAssertTrue(returned_info.ParseFromString(ToString(iter->second)));
451}
452
453- (void)testExpectError {
454  auto stub = [self getStub];
455  std::vector<ErrorStatus> expected_status;
456  expected_status.emplace_back();
457  expected_status.back().set_code(13);  // INTERNAL
458  // No Error message or details
459
460  expected_status.emplace_back();
461  expected_status.back().set_code(13);  // INTERNAL
462  expected_status.back().set_error_message("text error message");
463  expected_status.back().set_binary_error_details("text error details");
464
465  expected_status.emplace_back();
466  expected_status.back().set_code(13);  // INTERNAL
467  expected_status.back().set_error_message("text error message");
468  expected_status.back().set_binary_error_details("\x0\x1\x2\x3\x4\x5\x6\x8\x9\xA\xB");
469
470  for (auto iter = expected_status.begin(); iter != expected_status.end(); ++iter) {
471    EchoRequest request;
472    EchoResponse response;
473    ClientContext context;
474    request.set_message("Hello");
475    auto* error = request.mutable_param()->mutable_expected_error();
476    error->set_code(iter->code());
477    error->set_error_message(iter->error_message());
478    error->set_binary_error_details(iter->binary_error_details());
479
480    Status s = stub->Echo(&context, request, &response);
481    XCTAssertFalse(s.ok());
482    XCTAssertEqual(iter->code(), s.error_code());
483    XCTAssertEqual(iter->error_message(), s.error_message());
484    XCTAssertEqual(iter->binary_error_details(), s.error_details());
485    XCTAssertTrue(context.debug_error_string().find("created") != std::string::npos);
486    XCTAssertTrue(context.debug_error_string().find("file") != std::string::npos);
487    XCTAssertTrue(context.debug_error_string().find("line") != std::string::npos);
488    XCTAssertTrue(context.debug_error_string().find("status") != std::string::npos);
489    XCTAssertTrue(context.debug_error_string().find("13") != std::string::npos);
490  }
491}
492
493- (void)testRpcDeadlineExpires {
494  auto stub = [self getStub];
495  EchoRequest request;
496  EchoResponse response;
497  request.set_message("Hello");
498  request.mutable_param()->set_skip_cancelled_check(true);
499  // Let server sleep for 40 ms first to guarantee expiry.
500  request.mutable_param()->set_server_sleep_us(40 * 1000);
501
502  ClientContext context;
503  std::chrono::system_clock::time_point deadline =
504      std::chrono::system_clock::now() + std::chrono::milliseconds(1);
505  context.set_deadline(deadline);
506  Status s = stub->Echo(&context, request, &response);
507  XCTAssertEqual(grpc::StatusCode::DEADLINE_EXCEEDED, s.error_code());
508}
509
510- (void)testRpcLongDeadline {
511  auto stub = [self getStub];
512  EchoRequest request;
513  EchoResponse response;
514  request.set_message("Hello");
515
516  ClientContext context;
517  std::chrono::system_clock::time_point deadline =
518      std::chrono::system_clock::now() + std::chrono::hours(1);
519  context.set_deadline(deadline);
520  Status s = stub->Echo(&context, request, &response);
521  XCTAssertEqual(response.message(), request.message());
522  XCTAssertTrue(s.ok());
523}
524
525- (void)testEchoDeadlineForNoDeadlineRpc {
526  auto stub = [self getStub];
527  EchoRequest request;
528  EchoResponse response;
529  request.set_message("Hello");
530  request.mutable_param()->set_echo_deadline(true);
531
532  ClientContext context;
533  Status s = stub->Echo(&context, request, &response);
534  XCTAssertEqual(response.message(), request.message());
535  XCTAssertTrue(s.ok());
536  XCTAssertEqual(response.param().request_deadline(), gpr_inf_future(GPR_CLOCK_REALTIME).tv_sec);
537}
538
539- (void)testEchoDeadline {
540  auto stub = [self getStub];
541  EchoRequest request;
542  EchoResponse response;
543  request.set_message("Hello");
544  request.mutable_param()->set_echo_deadline(true);
545
546  ClientContext context;
547  std::chrono::system_clock::time_point deadline =
548      std::chrono::system_clock::now() + std::chrono::seconds(100);
549  context.set_deadline(deadline);
550  Status s = stub->Echo(&context, request, &response);
551  XCTAssertEqual(response.message(), request.message());
552  XCTAssertTrue(s.ok());
553  gpr_timespec sent_deadline;
554  grpc::Timepoint2Timespec(deadline, &sent_deadline);
555  // We want to allow some reasonable error given:
556  // - request_deadline() only has 1sec resolution so the best we can do is +-1
557  // - if sent_deadline.tv_nsec is very close to the next second's boundary we
558  // can end up being off by 2 in one direction.
559  XCTAssertLessThanOrEqual(response.param().request_deadline() - sent_deadline.tv_sec, 2);
560  XCTAssertGreaterThanOrEqual(response.param().request_deadline() - sent_deadline.tv_sec, -1);
561  NSLog(@"request deadline: %d sent_deadline: %d", response.param().request_deadline(),
562        sent_deadline.tv_sec);
563}
564
565- (void)testPeer {
566  auto stub = [self getStub];
567  EchoRequest request;
568  EchoResponse response;
569  request.set_message("Hello");
570  ClientContext context;
571  Status s = stub->Echo(&context, request, &response);
572  XCTAssertTrue(s.ok());
573  XCTAssertTrue(CheckIsLocalhost(context.peer()));
574}
575
576- (void)testClientInterceptor {
577  PhonyInterceptor::Reset();
578  std::vector<std::unique_ptr<grpc::experimental::ClientInterceptorFactoryInterface>> creators;
579  // Add 20 phony interceptors
580  for (auto i = 0; i < 20; i++) {
581    creators.push_back(std::unique_ptr<PhonyInterceptorFactory>(new PhonyInterceptorFactory()));
582  }
583  auto channel = [self getChannelWithInterceptors:std::move(creators)];
584  auto stub = EchoTestService::NewStub(channel);
585
586  EchoRequest request;
587  EchoResponse response;
588  ClientContext context;
589  request.set_message("Hello");
590  Status s = stub->Echo(&context, request, &response);
591  XCTAssertTrue(s.ok());
592  XCTAssertEqual(PhonyInterceptor::GetNumTimesRun(), 20);
593}
594
595@end
596