1/* 2 * 3 * Copyright 2018 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 <GRPCClient/GRPCCall.h> 20#import <ProtoRPC/ProtoMethod.h> 21#import <XCTest/XCTest.h> 22#import "src/objective-c/tests/RemoteTestClient/Messages.pbobjc.h" 23 24#include <grpc/grpc.h> 25#include <grpc/support/port_platform.h> 26 27#import "../version.h" 28 29// The server address is derived from preprocessor macro, which is 30// in turn derived from environment variable of the same name. 31#define NSStringize_helper(x) #x 32#define NSStringize(x) @NSStringize_helper(x) 33static NSString *const kHostAddress = NSStringize(HOST_PORT_LOCAL); 34static NSString *const kRemoteSSLHost = NSStringize(HOST_PORT_REMOTE); 35 36// Package and service name of test server 37static NSString *const kPackage = @"grpc.testing"; 38static NSString *const kService = @"TestService"; 39 40static GRPCProtoMethod *kInexistentMethod; 41static GRPCProtoMethod *kEmptyCallMethod; 42static GRPCProtoMethod *kUnaryCallMethod; 43static GRPCProtoMethod *kOutputStreamingCallMethod; 44static GRPCProtoMethod *kFullDuplexCallMethod; 45 46static const int kSimpleDataLength = 100; 47 48static const NSTimeInterval kTestTimeout = 8; 49static const NSTimeInterval kInvertedTimeout = 2; 50 51// Reveal the _class ivar for testing access 52@interface GRPCCall2 () { 53 @public 54 GRPCCall *_call; 55} 56 57@end 58 59// Convenience class to use blocks as callbacks 60@interface ClientTestsBlockCallbacks : NSObject <GRPCResponseHandler> 61 62- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback 63 messageCallback:(void (^)(id))messageCallback 64 closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback 65 writeDataCallback:(void (^)(void))writeDataCallback; 66 67- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback 68 messageCallback:(void (^)(id))messageCallback 69 closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback; 70 71@end 72 73@implementation ClientTestsBlockCallbacks { 74 void (^_initialMetadataCallback)(NSDictionary *); 75 void (^_messageCallback)(id); 76 void (^_closeCallback)(NSDictionary *, NSError *); 77 void (^_writeDataCallback)(void); 78 dispatch_queue_t _dispatchQueue; 79} 80 81- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback 82 messageCallback:(void (^)(id))messageCallback 83 closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback 84 writeDataCallback:(void (^)(void))writeDataCallback { 85 if ((self = [super init])) { 86 _initialMetadataCallback = initialMetadataCallback; 87 _messageCallback = messageCallback; 88 _closeCallback = closeCallback; 89 _writeDataCallback = writeDataCallback; 90 _dispatchQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL); 91 } 92 return self; 93} 94 95- (instancetype)initWithInitialMetadataCallback:(void (^)(NSDictionary *))initialMetadataCallback 96 messageCallback:(void (^)(id))messageCallback 97 closeCallback:(void (^)(NSDictionary *, NSError *))closeCallback { 98 return [self initWithInitialMetadataCallback:initialMetadataCallback 99 messageCallback:messageCallback 100 closeCallback:closeCallback 101 writeDataCallback:nil]; 102} 103 104- (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata { 105 if (self->_initialMetadataCallback) { 106 self->_initialMetadataCallback(initialMetadata); 107 } 108} 109 110- (void)didReceiveRawMessage:(GPBMessage *)message { 111 if (self->_messageCallback) { 112 self->_messageCallback(message); 113 } 114} 115 116- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error { 117 if (self->_closeCallback) { 118 self->_closeCallback(trailingMetadata, error); 119 } 120} 121 122- (void)didWriteData { 123 if (self->_writeDataCallback) { 124 self->_writeDataCallback(); 125 } 126} 127 128- (dispatch_queue_t)dispatchQueue { 129 return _dispatchQueue; 130} 131 132@end 133 134@interface CallAPIv2Tests : XCTestCase <GRPCAuthorizationProtocol> 135 136@end 137 138@implementation CallAPIv2Tests 139 140- (void)setUp { 141 // This method isn't implemented by the remote server. 142 kInexistentMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 143 service:kService 144 method:@"Inexistent"]; 145 kEmptyCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 146 service:kService 147 method:@"EmptyCall"]; 148 kUnaryCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 149 service:kService 150 method:@"UnaryCall"]; 151 kOutputStreamingCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 152 service:kService 153 method:@"StreamingOutputCall"]; 154 kFullDuplexCallMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage 155 service:kService 156 method:@"FullDuplexCall"]; 157} 158 159- (void)testMetadata { 160 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"RPC unauthorized."]; 161 162 RMTSimpleRequest *request = [RMTSimpleRequest message]; 163 request.fillUsername = YES; 164 request.fillOauthScope = YES; 165 166 GRPCRequestOptions *callRequest = 167 [[GRPCRequestOptions alloc] initWithHost:(NSString *)kRemoteSSLHost 168 path:kUnaryCallMethod.HTTPPath 169 safety:GRPCCallSafetyDefault]; 170 __block NSDictionary *init_md; 171 __block NSDictionary *trailing_md; 172 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 173 options.oauth2AccessToken = @"bogusToken"; 174 GRPCCall2 *call = [[GRPCCall2 alloc] 175 initWithRequestOptions:callRequest 176 responseHandler:[[ClientTestsBlockCallbacks alloc] 177 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) { 178 init_md = initialMetadata; 179 } 180 messageCallback:^(id message) { 181 XCTFail(@"Received unexpected response."); 182 } 183 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { 184 trailing_md = trailingMetadata; 185 if (error) { 186 XCTAssertEqual(error.code, 16, 187 @"Finished with unexpected error: %@", error); 188 XCTAssertEqualObjects(init_md, 189 error.userInfo[kGRPCHeadersKey]); 190 XCTAssertEqualObjects(trailing_md, 191 error.userInfo[kGRPCTrailersKey]); 192 NSString *challengeHeader = init_md[@"www-authenticate"]; 193 XCTAssertGreaterThan(challengeHeader.length, 0, 194 @"No challenge in response headers %@", 195 init_md); 196 [expectation fulfill]; 197 } 198 }] 199 callOptions:options]; 200 201 [call start]; 202 [call writeData:[request data]]; 203 [call finish]; 204 205 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 206} 207 208- (void)testUserAgentPrefix { 209 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."]; 210 __weak XCTestExpectation *recvInitialMd = 211 [self expectationWithDescription:@"Did not receive initial md."]; 212 213 GRPCRequestOptions *request = [[GRPCRequestOptions alloc] initWithHost:kHostAddress 214 path:kEmptyCallMethod.HTTPPath 215 safety:GRPCCallSafetyDefault]; 216 NSDictionary *headers = 217 [NSDictionary dictionaryWithObjectsAndKeys:@"", @"x-grpc-test-echo-useragent", nil]; 218 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 219 options.transportType = GRPCTransportTypeInsecure; 220 options.userAgentPrefix = @"Foo"; 221 options.initialMetadata = headers; 222 GRPCCall2 *call = [[GRPCCall2 alloc] 223 initWithRequestOptions:request 224 responseHandler: 225 [[ClientTestsBlockCallbacks alloc] 226 initWithInitialMetadataCallback:^(NSDictionary *initialMetadata) { 227 NSString *userAgent = initialMetadata[@"x-grpc-test-echo-useragent"]; 228 // Test the regex is correct 229 NSString *expectedUserAgent = @"Foo grpc-objc-cfstream/"; 230 expectedUserAgent = 231 [expectedUserAgent stringByAppendingString:GRPC_OBJC_VERSION_STRING]; 232 expectedUserAgent = [expectedUserAgent stringByAppendingString:@" grpc-c/"]; 233 expectedUserAgent = 234 [expectedUserAgent stringByAppendingString:GRPC_C_VERSION_STRING]; 235 expectedUserAgent = [expectedUserAgent stringByAppendingString:@" ("]; 236 expectedUserAgent = 237 [expectedUserAgent stringByAppendingString:@GPR_PLATFORM_STRING]; 238 expectedUserAgent = [expectedUserAgent stringByAppendingString:@"; chttp2)"]; 239 XCTAssertEqualObjects(userAgent, expectedUserAgent); 240 241 NSError *error = nil; 242 // Change in format of user-agent field in a direction that does not match 243 // the regex will likely cause problem for certain gRPC users. For details, 244 // refer to internal doc https://goo.gl/c2diBc 245 NSRegularExpression *regex = [NSRegularExpression 246 regularExpressionWithPattern: 247 @" grpc-[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?/[^ ,]+( \\([^)]*\\))?" 248 options:0 249 error:&error]; 250 251 NSString *customUserAgent = [regex 252 stringByReplacingMatchesInString:userAgent 253 options:0 254 range:NSMakeRange(0, [userAgent length]) 255 withTemplate:@""]; 256 XCTAssertEqualObjects(customUserAgent, @"Foo"); 257 [recvInitialMd fulfill]; 258 } 259 messageCallback:^(id message) { 260 XCTAssertNotNil(message); 261 XCTAssertEqual([message length], 0, @"Non-empty response received: %@", 262 message); 263 } 264 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { 265 if (error) { 266 XCTFail(@"Finished with unexpected error: %@", error); 267 } else { 268 [completion fulfill]; 269 } 270 }] 271 callOptions:options]; 272 [call writeData:[NSData data]]; 273 [call start]; 274 275 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 276} 277 278- (void)getTokenWithHandler:(void (^)(NSString *token))handler { 279 dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 280 dispatch_sync(queue, ^{ 281 handler(@"test-access-token"); 282 }); 283} 284 285- (void)testOAuthToken { 286 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; 287 288 GRPCRequestOptions *requestOptions = 289 [[GRPCRequestOptions alloc] initWithHost:kHostAddress 290 path:kEmptyCallMethod.HTTPPath 291 safety:GRPCCallSafetyDefault]; 292 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 293 options.transportType = GRPCTransportTypeInsecure; 294 options.authTokenProvider = self; 295 __block GRPCCall2 *call = [[GRPCCall2 alloc] 296 initWithRequestOptions:requestOptions 297 responseHandler:[[ClientTestsBlockCallbacks alloc] 298 initWithInitialMetadataCallback:nil 299 messageCallback:nil 300 closeCallback:^(NSDictionary *trailingMetadata, 301 NSError *error) { 302 [completion fulfill]; 303 }] 304 callOptions:options]; 305 [call writeData:[NSData data]]; 306 [call start]; 307 [call finish]; 308 309 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 310} 311 312- (void)testResponseSizeLimitExceeded { 313 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; 314 315 GRPCRequestOptions *requestOptions = 316 [[GRPCRequestOptions alloc] initWithHost:kHostAddress 317 path:kUnaryCallMethod.HTTPPath 318 safety:GRPCCallSafetyDefault]; 319 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 320 options.responseSizeLimit = kSimpleDataLength; 321 options.transportType = GRPCTransportTypeInsecure; 322 323 RMTSimpleRequest *request = [RMTSimpleRequest message]; 324 request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit]; 325 request.responseSize = (int32_t)(options.responseSizeLimit * 2); 326 327 GRPCCall2 *call = [[GRPCCall2 alloc] 328 initWithRequestOptions:requestOptions 329 responseHandler:[[ClientTestsBlockCallbacks alloc] 330 initWithInitialMetadataCallback:nil 331 messageCallback:nil 332 closeCallback:^(NSDictionary *trailingMetadata, 333 NSError *error) { 334 XCTAssertNotNil(error, 335 @"Expecting non-nil error"); 336 XCTAssertEqual(error.code, 337 GRPCErrorCodeResourceExhausted); 338 [completion fulfill]; 339 }] 340 callOptions:options]; 341 [call writeData:[request data]]; 342 [call start]; 343 [call finish]; 344 345 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 346} 347 348- (void)testIdempotentProtoRPC { 349 __weak XCTestExpectation *response = [self expectationWithDescription:@"Expected response."]; 350 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; 351 352 RMTSimpleRequest *request = [RMTSimpleRequest message]; 353 request.responseSize = kSimpleDataLength; 354 request.fillUsername = YES; 355 request.fillOauthScope = YES; 356 GRPCRequestOptions *requestOptions = 357 [[GRPCRequestOptions alloc] initWithHost:kHostAddress 358 path:kUnaryCallMethod.HTTPPath 359 safety:GRPCCallSafetyIdempotentRequest]; 360 361 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 362 options.transportType = GRPCTransportTypeInsecure; 363 GRPCCall2 *call = [[GRPCCall2 alloc] 364 initWithRequestOptions:requestOptions 365 responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 366 messageCallback:^(id message) { 367 NSData *data = (NSData *)message; 368 XCTAssertNotNil(data, @"nil value received as response."); 369 XCTAssertGreaterThan(data.length, 0, 370 @"Empty response received."); 371 RMTSimpleResponse *responseProto = 372 [RMTSimpleResponse parseFromData:data error:NULL]; 373 // We expect empty strings, not nil: 374 XCTAssertNotNil(responseProto.username, 375 @"Response's username is nil."); 376 XCTAssertNotNil(responseProto.oauthScope, 377 @"Response's OAuth scope is nil."); 378 [response fulfill]; 379 } 380 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { 381 XCTAssertNil(error, @"Finished with unexpected error: %@", 382 error); 383 [completion fulfill]; 384 }] 385 callOptions:options]; 386 387 [call start]; 388 [call writeData:[request data]]; 389 [call finish]; 390 391 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 392} 393 394- (void)testTimeout { 395 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; 396 397 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 398 options.timeout = 0.001; 399 options.transportType = GRPCTransportTypeInsecure; 400 GRPCRequestOptions *requestOptions = 401 [[GRPCRequestOptions alloc] initWithHost:kHostAddress 402 path:kFullDuplexCallMethod.HTTPPath 403 safety:GRPCCallSafetyDefault]; 404 405 GRPCCall2 *call = [[GRPCCall2 alloc] 406 initWithRequestOptions:requestOptions 407 responseHandler: 408 [[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 409 messageCallback:^(NSData *data) { 410 XCTFail(@"Failure: response received; Expect: no response received."); 411 } 412 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { 413 XCTAssertNotNil(error, @"Failure: no error received; Expect: receive " 414 @"deadline exceeded."); 415 if (error.code != GRPCErrorCodeDeadlineExceeded) { 416 NSLog(@"Unexpected error: %@", error); 417 } 418 XCTAssertEqual(error.code, GRPCErrorCodeDeadlineExceeded); 419 [completion fulfill]; 420 }] 421 callOptions:options]; 422 423 [call start]; 424 425 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 426} 427 428- (void)testTimeoutBackoffWithTimeout:(double)timeout Backoff:(double)backoff { 429 const double maxConnectTime = timeout > backoff ? timeout : backoff; 430 const double kMargin = 0.1; 431 432 __weak XCTestExpectation *completion = [self expectationWithDescription:@"Timeout in a second."]; 433 NSString *const kDummyAddress = [NSString stringWithFormat:@"127.0.0.1:10000"]; 434 GRPCRequestOptions *requestOptions = 435 [[GRPCRequestOptions alloc] initWithHost:kDummyAddress 436 path:@"/dummy/path" 437 safety:GRPCCallSafetyDefault]; 438 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 439 options.connectMinTimeout = timeout; 440 options.connectInitialBackoff = backoff; 441 options.connectMaxBackoff = 0; 442 443 NSDate *startTime = [NSDate date]; 444 GRPCCall2 *call = [[GRPCCall2 alloc] 445 initWithRequestOptions:requestOptions 446 responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 447 messageCallback:^(NSData *data) { 448 XCTFail(@"Received message. Should not reach here."); 449 } 450 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { 451 XCTAssertNotNil(error, 452 @"Finished with no error; expecting error"); 453 XCTAssertLessThan( 454 [[NSDate date] timeIntervalSinceDate:startTime], 455 maxConnectTime + kMargin); 456 [completion fulfill]; 457 }] 458 callOptions:options]; 459 460 [call start]; 461 462 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 463} 464 465- (void)testTimeoutBackoff1 { 466 [self testTimeoutBackoffWithTimeout:0.7 Backoff:0.4]; 467} 468 469- (void)testTimeoutBackoff2 { 470 [self testTimeoutBackoffWithTimeout:0.3 Backoff:0.8]; 471} 472 473- (void)testCompression { 474 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; 475 476 RMTSimpleRequest *request = [RMTSimpleRequest message]; 477 request.expectCompressed = [RMTBoolValue message]; 478 request.expectCompressed.value = YES; 479 request.responseCompressed = [RMTBoolValue message]; 480 request.expectCompressed.value = YES; 481 request.responseSize = kSimpleDataLength; 482 request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength]; 483 GRPCRequestOptions *requestOptions = 484 [[GRPCRequestOptions alloc] initWithHost:kHostAddress 485 path:kUnaryCallMethod.HTTPPath 486 safety:GRPCCallSafetyDefault]; 487 488 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 489 options.transportType = GRPCTransportTypeInsecure; 490 options.compressionAlgorithm = GRPCCompressGzip; 491 GRPCCall2 *call = [[GRPCCall2 alloc] 492 initWithRequestOptions:requestOptions 493 responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 494 messageCallback:^(NSData *data) { 495 NSError *error; 496 RMTSimpleResponse *response = 497 [RMTSimpleResponse parseFromData:data error:&error]; 498 XCTAssertNil(error, @"Error when parsing response: %@", error); 499 XCTAssertEqual(response.payload.body.length, kSimpleDataLength); 500 } 501 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { 502 XCTAssertNil(error, @"Received failure: %@", error); 503 [completion fulfill]; 504 }] 505 506 callOptions:options]; 507 508 [call start]; 509 [call writeData:[request data]]; 510 [call finish]; 511 512 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 513} 514 515- (void)testFlowControlWrite { 516 __weak XCTestExpectation *expectWriteData = 517 [self expectationWithDescription:@"Reported write data"]; 518 519 RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message]; 520 RMTResponseParameters *parameters = [RMTResponseParameters message]; 521 parameters.size = kSimpleDataLength; 522 [request.responseParametersArray addObject:parameters]; 523 request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength]; 524 525 GRPCRequestOptions *callRequest = 526 [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress 527 path:kUnaryCallMethod.HTTPPath 528 safety:GRPCCallSafetyDefault]; 529 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 530 options.transportType = GRPCTransportTypeInsecure; 531 options.flowControlEnabled = YES; 532 GRPCCall2 *call = 533 [[GRPCCall2 alloc] initWithRequestOptions:callRequest 534 responseHandler:[[ClientTestsBlockCallbacks alloc] 535 initWithInitialMetadataCallback:nil 536 messageCallback:nil 537 closeCallback:nil 538 writeDataCallback:^{ 539 [expectWriteData fulfill]; 540 }] 541 callOptions:options]; 542 543 [call start]; 544 [call receiveNextMessages:1]; 545 [call writeData:[request data]]; 546 547 // Wait for 3 seconds and make sure we do not receive the response 548 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 549 550 [call finish]; 551} 552 553- (void)testFlowControlRead { 554 __weak __block XCTestExpectation *expectBlockedMessage = 555 [self expectationWithDescription:@"Message not delivered without recvNextMessage"]; 556 __weak __block XCTestExpectation *expectPassedMessage = nil; 557 __weak __block XCTestExpectation *expectBlockedClose = 558 [self expectationWithDescription:@"Call not closed with pending message"]; 559 __weak __block XCTestExpectation *expectPassedClose = nil; 560 expectBlockedMessage.inverted = YES; 561 expectBlockedClose.inverted = YES; 562 563 RMTSimpleRequest *request = [RMTSimpleRequest message]; 564 request.responseSize = kSimpleDataLength; 565 request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength]; 566 567 GRPCRequestOptions *callRequest = 568 [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress 569 path:kUnaryCallMethod.HTTPPath 570 safety:GRPCCallSafetyDefault]; 571 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 572 options.transportType = GRPCTransportTypeInsecure; 573 options.flowControlEnabled = YES; 574 __block int unblocked = NO; 575 GRPCCall2 *call = [[GRPCCall2 alloc] 576 initWithRequestOptions:callRequest 577 responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 578 messageCallback:^(NSData *message) { 579 if (!unblocked) { 580 [expectBlockedMessage fulfill]; 581 } else { 582 [expectPassedMessage fulfill]; 583 } 584 } 585 closeCallback:^(NSDictionary *trailers, NSError *error) { 586 if (!unblocked) { 587 [expectBlockedClose fulfill]; 588 } else { 589 [expectPassedClose fulfill]; 590 } 591 }] 592 callOptions:options]; 593 594 [call start]; 595 [call writeData:[request data]]; 596 [call finish]; 597 598 // Wait to make sure we do not receive the response 599 [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil]; 600 601 expectPassedMessage = 602 [self expectationWithDescription:@"Message delivered with receiveNextMessage"]; 603 expectPassedClose = [self expectationWithDescription:@"Close delivered after receiveNextMessage"]; 604 605 unblocked = YES; 606 [call receiveNextMessages:1]; 607 608 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 609} 610 611- (void)testFlowControlMultipleMessages { 612 __weak XCTestExpectation *expectPassedMessage = 613 [self expectationWithDescription:@"two messages delivered with receiveNextMessage"]; 614 expectPassedMessage.expectedFulfillmentCount = 2; 615 __weak XCTestExpectation *expectBlockedMessage = 616 [self expectationWithDescription:@"Message 3 not delivered"]; 617 expectBlockedMessage.inverted = YES; 618 __weak XCTestExpectation *expectWriteTwice = 619 [self expectationWithDescription:@"Write 2 messages done"]; 620 expectWriteTwice.expectedFulfillmentCount = 2; 621 622 RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message]; 623 RMTResponseParameters *parameters = [RMTResponseParameters message]; 624 parameters.size = kSimpleDataLength; 625 [request.responseParametersArray addObject:parameters]; 626 request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength]; 627 628 GRPCRequestOptions *callRequest = 629 [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress 630 path:kFullDuplexCallMethod.HTTPPath 631 safety:GRPCCallSafetyDefault]; 632 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 633 options.transportType = GRPCTransportTypeInsecure; 634 options.flowControlEnabled = YES; 635 __block NSUInteger messageId = 0; 636 __block GRPCCall2 *call = [[GRPCCall2 alloc] 637 initWithRequestOptions:callRequest 638 responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 639 messageCallback:^(NSData *message) { 640 if (messageId <= 1) { 641 [expectPassedMessage fulfill]; 642 } else { 643 [expectBlockedMessage fulfill]; 644 } 645 messageId++; 646 } 647 closeCallback:nil 648 writeDataCallback:^{ 649 [expectWriteTwice fulfill]; 650 }] 651 callOptions:options]; 652 653 [call receiveNextMessages:2]; 654 [call start]; 655 [call writeData:[request data]]; 656 [call writeData:[request data]]; 657 658 [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil]; 659} 660 661- (void)testFlowControlReadReadyBeforeStart { 662 __weak XCTestExpectation *expectPassedMessage = 663 [self expectationWithDescription:@"Message delivered with receiveNextMessage"]; 664 __weak XCTestExpectation *expectPassedClose = 665 [self expectationWithDescription:@"Close delivered with receiveNextMessage"]; 666 667 RMTSimpleRequest *request = [RMTSimpleRequest message]; 668 request.responseSize = kSimpleDataLength; 669 request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength]; 670 671 GRPCRequestOptions *callRequest = 672 [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress 673 path:kUnaryCallMethod.HTTPPath 674 safety:GRPCCallSafetyDefault]; 675 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 676 options.transportType = GRPCTransportTypeInsecure; 677 options.flowControlEnabled = YES; 678 __block BOOL closed = NO; 679 GRPCCall2 *call = [[GRPCCall2 alloc] 680 initWithRequestOptions:callRequest 681 responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 682 messageCallback:^(NSData *message) { 683 [expectPassedMessage fulfill]; 684 XCTAssertFalse(closed); 685 } 686 closeCallback:^(NSDictionary *ttrailers, NSError *error) { 687 closed = YES; 688 [expectPassedClose fulfill]; 689 }] 690 callOptions:options]; 691 692 [call receiveNextMessages:1]; 693 [call start]; 694 [call writeData:[request data]]; 695 [call finish]; 696 697 [self waitForExpectationsWithTimeout:kInvertedTimeout handler:nil]; 698} 699 700- (void)testFlowControlReadReadyAfterStart { 701 __weak XCTestExpectation *expectPassedMessage = 702 [self expectationWithDescription:@"Message delivered with receiveNextMessage"]; 703 __weak XCTestExpectation *expectPassedClose = 704 [self expectationWithDescription:@"Close delivered with receiveNextMessage"]; 705 706 RMTStreamingOutputCallRequest *request = [RMTStreamingOutputCallRequest message]; 707 RMTResponseParameters *parameters = [RMTResponseParameters message]; 708 parameters.size = kSimpleDataLength; 709 [request.responseParametersArray addObject:parameters]; 710 request.payload.body = [NSMutableData dataWithLength:kSimpleDataLength]; 711 712 GRPCRequestOptions *callRequest = 713 [[GRPCRequestOptions alloc] initWithHost:(NSString *)kHostAddress 714 path:kUnaryCallMethod.HTTPPath 715 safety:GRPCCallSafetyDefault]; 716 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 717 options.transportType = GRPCTransportTypeInsecure; 718 options.flowControlEnabled = YES; 719 __block BOOL closed = NO; 720 GRPCCall2 *call = [[GRPCCall2 alloc] 721 initWithRequestOptions:callRequest 722 responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 723 messageCallback:^(NSData *message) { 724 [expectPassedMessage fulfill]; 725 XCTAssertFalse(closed); 726 } 727 closeCallback:^(NSDictionary *trailers, NSError *error) { 728 closed = YES; 729 [expectPassedClose fulfill]; 730 }] 731 callOptions:options]; 732 733 [call start]; 734 [call receiveNextMessages:1]; 735 [call writeData:[request data]]; 736 [call finish]; 737 738 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 739} 740 741- (void)testFlowControlReadNonBlockingFailure { 742 __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."]; 743 744 GRPCRequestOptions *requestOptions = 745 [[GRPCRequestOptions alloc] initWithHost:kHostAddress 746 path:kUnaryCallMethod.HTTPPath 747 safety:GRPCCallSafetyDefault]; 748 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 749 options.flowControlEnabled = YES; 750 options.transportType = GRPCTransportTypeInsecure; 751 752 RMTSimpleRequest *request = [RMTSimpleRequest message]; 753 request.payload.body = [NSMutableData dataWithLength:options.responseSizeLimit]; 754 755 RMTEchoStatus *status = [RMTEchoStatus message]; 756 status.code = 2; 757 status.message = @"test"; 758 request.responseStatus = status; 759 760 GRPCCall2 *call = [[GRPCCall2 alloc] 761 initWithRequestOptions:requestOptions 762 responseHandler:[[ClientTestsBlockCallbacks alloc] initWithInitialMetadataCallback:nil 763 messageCallback:^(NSData *data) { 764 XCTFail(@"Received unexpected message"); 765 } 766 closeCallback:^(NSDictionary *trailingMetadata, NSError *error) { 767 XCTAssertNotNil(error, @"Expecting non-nil error"); 768 XCTAssertEqual(error.code, 2); 769 [completion fulfill]; 770 }] 771 callOptions:options]; 772 [call writeData:[request data]]; 773 [call start]; 774 [call finish]; 775 776 [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; 777} 778 779@end 780