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