1*cc02d7e2SAndroid Build Coastguard Worker/* 2*cc02d7e2SAndroid Build Coastguard Worker * 3*cc02d7e2SAndroid Build Coastguard Worker * Copyright 2015 gRPC authors. 4*cc02d7e2SAndroid Build Coastguard Worker * 5*cc02d7e2SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 6*cc02d7e2SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 7*cc02d7e2SAndroid Build Coastguard Worker * You may obtain a copy of the License at 8*cc02d7e2SAndroid Build Coastguard Worker * 9*cc02d7e2SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 10*cc02d7e2SAndroid Build Coastguard Worker * 11*cc02d7e2SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 12*cc02d7e2SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 13*cc02d7e2SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*cc02d7e2SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 15*cc02d7e2SAndroid Build Coastguard Worker * limitations under the License. 16*cc02d7e2SAndroid Build Coastguard Worker * 17*cc02d7e2SAndroid Build Coastguard Worker */ 18*cc02d7e2SAndroid Build Coastguard Worker 19*cc02d7e2SAndroid Build Coastguard Worker#import "GRPCCall.h" 20*cc02d7e2SAndroid Build Coastguard Worker 21*cc02d7e2SAndroid Build Coastguard Worker#import "GRPCCall+Interceptor.h" 22*cc02d7e2SAndroid Build Coastguard Worker#import "GRPCCallOptions.h" 23*cc02d7e2SAndroid Build Coastguard Worker#import "GRPCInterceptor.h" 24*cc02d7e2SAndroid Build Coastguard Worker 25*cc02d7e2SAndroid Build Coastguard Worker#import "GRPCTransport.h" 26*cc02d7e2SAndroid Build Coastguard Worker#import "private/GRPCTransport+Private.h" 27*cc02d7e2SAndroid Build Coastguard Worker 28*cc02d7e2SAndroid Build Coastguard Worker/** 29*cc02d7e2SAndroid Build Coastguard Worker * The response dispatcher creates its own serial dispatch queue and target the queue to the 30*cc02d7e2SAndroid Build Coastguard Worker * dispatch queue of a user provided response handler. It removes the requirement of having to use 31*cc02d7e2SAndroid Build Coastguard Worker * serial dispatch queue in the user provided response handler. 32*cc02d7e2SAndroid Build Coastguard Worker */ 33*cc02d7e2SAndroid Build Coastguard Worker@interface GRPCResponseDispatcher : NSObject <GRPCResponseHandler> 34*cc02d7e2SAndroid Build Coastguard Worker 35*cc02d7e2SAndroid Build Coastguard Worker- (nullable instancetype)initWithResponseHandler:(id<GRPCResponseHandler>)responseHandler; 36*cc02d7e2SAndroid Build Coastguard Worker 37*cc02d7e2SAndroid Build Coastguard Worker@end 38*cc02d7e2SAndroid Build Coastguard Worker 39*cc02d7e2SAndroid Build Coastguard Worker@implementation GRPCResponseDispatcher { 40*cc02d7e2SAndroid Build Coastguard Worker id<GRPCResponseHandler> _responseHandler; 41*cc02d7e2SAndroid Build Coastguard Worker dispatch_queue_t _dispatchQueue; 42*cc02d7e2SAndroid Build Coastguard Worker} 43*cc02d7e2SAndroid Build Coastguard Worker 44*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)initWithResponseHandler:(id<GRPCResponseHandler>)responseHandler { 45*cc02d7e2SAndroid Build Coastguard Worker if ((self = [super init])) { 46*cc02d7e2SAndroid Build Coastguard Worker _responseHandler = responseHandler; 47*cc02d7e2SAndroid Build Coastguard Worker#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 48*cc02d7e2SAndroid Build Coastguard Worker if (@available(iOS 8.0, macOS 10.10, *)) { 49*cc02d7e2SAndroid Build Coastguard Worker _dispatchQueue = dispatch_queue_create( 50*cc02d7e2SAndroid Build Coastguard Worker NULL, 51*cc02d7e2SAndroid Build Coastguard Worker dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0)); 52*cc02d7e2SAndroid Build Coastguard Worker } else { 53*cc02d7e2SAndroid Build Coastguard Worker#else 54*cc02d7e2SAndroid Build Coastguard Worker { 55*cc02d7e2SAndroid Build Coastguard Worker#endif 56*cc02d7e2SAndroid Build Coastguard Worker _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 57*cc02d7e2SAndroid Build Coastguard Worker } 58*cc02d7e2SAndroid Build Coastguard Worker dispatch_set_target_queue(_dispatchQueue, _responseHandler.dispatchQueue); 59*cc02d7e2SAndroid Build Coastguard Worker } 60*cc02d7e2SAndroid Build Coastguard Worker 61*cc02d7e2SAndroid Build Coastguard Worker return self; 62*cc02d7e2SAndroid Build Coastguard Worker} 63*cc02d7e2SAndroid Build Coastguard Worker 64*cc02d7e2SAndroid Build Coastguard Worker- (dispatch_queue_t)dispatchQueue { 65*cc02d7e2SAndroid Build Coastguard Worker return _dispatchQueue; 66*cc02d7e2SAndroid Build Coastguard Worker} 67*cc02d7e2SAndroid Build Coastguard Worker 68*cc02d7e2SAndroid Build Coastguard Worker- (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata { 69*cc02d7e2SAndroid Build Coastguard Worker if ([_responseHandler respondsToSelector:@selector(didReceiveInitialMetadata:)]) { 70*cc02d7e2SAndroid Build Coastguard Worker [_responseHandler didReceiveInitialMetadata:initialMetadata]; 71*cc02d7e2SAndroid Build Coastguard Worker } 72*cc02d7e2SAndroid Build Coastguard Worker} 73*cc02d7e2SAndroid Build Coastguard Worker 74*cc02d7e2SAndroid Build Coastguard Worker- (void)didReceiveData:(id)data { 75*cc02d7e2SAndroid Build Coastguard Worker // For backwards compatibility with didReceiveRawMessage, if the user provided a response handler 76*cc02d7e2SAndroid Build Coastguard Worker // that handles didReceiveRawMesssage, we issue to that method instead 77*cc02d7e2SAndroid Build Coastguard Worker if ([_responseHandler respondsToSelector:@selector(didReceiveRawMessage:)]) { 78*cc02d7e2SAndroid Build Coastguard Worker [_responseHandler didReceiveRawMessage:data]; 79*cc02d7e2SAndroid Build Coastguard Worker } else if ([_responseHandler respondsToSelector:@selector(didReceiveData:)]) { 80*cc02d7e2SAndroid Build Coastguard Worker [_responseHandler didReceiveData:data]; 81*cc02d7e2SAndroid Build Coastguard Worker } 82*cc02d7e2SAndroid Build Coastguard Worker} 83*cc02d7e2SAndroid Build Coastguard Worker 84*cc02d7e2SAndroid Build Coastguard Worker- (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata 85*cc02d7e2SAndroid Build Coastguard Worker error:(nullable NSError *)error { 86*cc02d7e2SAndroid Build Coastguard Worker if ([_responseHandler respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { 87*cc02d7e2SAndroid Build Coastguard Worker [_responseHandler didCloseWithTrailingMetadata:trailingMetadata error:error]; 88*cc02d7e2SAndroid Build Coastguard Worker } 89*cc02d7e2SAndroid Build Coastguard Worker} 90*cc02d7e2SAndroid Build Coastguard Worker 91*cc02d7e2SAndroid Build Coastguard Worker- (void)didWriteData { 92*cc02d7e2SAndroid Build Coastguard Worker if ([_responseHandler respondsToSelector:@selector(didWriteData)]) { 93*cc02d7e2SAndroid Build Coastguard Worker [_responseHandler didWriteData]; 94*cc02d7e2SAndroid Build Coastguard Worker } 95*cc02d7e2SAndroid Build Coastguard Worker} 96*cc02d7e2SAndroid Build Coastguard Worker 97*cc02d7e2SAndroid Build Coastguard Worker@end 98*cc02d7e2SAndroid Build Coastguard Worker 99*cc02d7e2SAndroid Build Coastguard Worker@implementation GRPCRequestOptions 100*cc02d7e2SAndroid Build Coastguard Worker 101*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)initWithHost:(NSString *)host path:(NSString *)path safety:(GRPCCallSafety)safety { 102*cc02d7e2SAndroid Build Coastguard Worker NSAssert(host.length != 0 && path.length != 0, @"host and path cannot be empty"); 103*cc02d7e2SAndroid Build Coastguard Worker if (host.length == 0 || path.length == 0) { 104*cc02d7e2SAndroid Build Coastguard Worker return nil; 105*cc02d7e2SAndroid Build Coastguard Worker } 106*cc02d7e2SAndroid Build Coastguard Worker if ((self = [super init])) { 107*cc02d7e2SAndroid Build Coastguard Worker _host = [host copy]; 108*cc02d7e2SAndroid Build Coastguard Worker _path = [path copy]; 109*cc02d7e2SAndroid Build Coastguard Worker _safety = safety; 110*cc02d7e2SAndroid Build Coastguard Worker } 111*cc02d7e2SAndroid Build Coastguard Worker return self; 112*cc02d7e2SAndroid Build Coastguard Worker} 113*cc02d7e2SAndroid Build Coastguard Worker 114*cc02d7e2SAndroid Build Coastguard Worker- (id)copyWithZone:(NSZone *)zone { 115*cc02d7e2SAndroid Build Coastguard Worker GRPCRequestOptions *request = [[GRPCRequestOptions alloc] initWithHost:_host 116*cc02d7e2SAndroid Build Coastguard Worker path:_path 117*cc02d7e2SAndroid Build Coastguard Worker safety:_safety]; 118*cc02d7e2SAndroid Build Coastguard Worker 119*cc02d7e2SAndroid Build Coastguard Worker return request; 120*cc02d7e2SAndroid Build Coastguard Worker} 121*cc02d7e2SAndroid Build Coastguard Worker 122*cc02d7e2SAndroid Build Coastguard Worker@end 123*cc02d7e2SAndroid Build Coastguard Worker 124*cc02d7e2SAndroid Build Coastguard Worker/** 125*cc02d7e2SAndroid Build Coastguard Worker * This class acts as a wrapper for interceptors 126*cc02d7e2SAndroid Build Coastguard Worker */ 127*cc02d7e2SAndroid Build Coastguard Worker@implementation GRPCCall2 { 128*cc02d7e2SAndroid Build Coastguard Worker /** The handler of responses. */ 129*cc02d7e2SAndroid Build Coastguard Worker id<GRPCResponseHandler> _responseHandler; 130*cc02d7e2SAndroid Build Coastguard Worker 131*cc02d7e2SAndroid Build Coastguard Worker /** 132*cc02d7e2SAndroid Build Coastguard Worker * Points to the first interceptor in the interceptor chain. 133*cc02d7e2SAndroid Build Coastguard Worker */ 134*cc02d7e2SAndroid Build Coastguard Worker id<GRPCInterceptorInterface> _firstInterceptor; 135*cc02d7e2SAndroid Build Coastguard Worker 136*cc02d7e2SAndroid Build Coastguard Worker /** 137*cc02d7e2SAndroid Build Coastguard Worker * The actual call options being used by this call. It is different from the user-provided 138*cc02d7e2SAndroid Build Coastguard Worker * call options when the user provided a NULL call options object. 139*cc02d7e2SAndroid Build Coastguard Worker */ 140*cc02d7e2SAndroid Build Coastguard Worker GRPCCallOptions *_actualCallOptions; 141*cc02d7e2SAndroid Build Coastguard Worker} 142*cc02d7e2SAndroid Build Coastguard Worker 143*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions 144*cc02d7e2SAndroid Build Coastguard Worker responseHandler:(id<GRPCResponseHandler>)responseHandler 145*cc02d7e2SAndroid Build Coastguard Worker callOptions:(GRPCCallOptions *)callOptions { 146*cc02d7e2SAndroid Build Coastguard Worker NSAssert(requestOptions.host.length != 0 && requestOptions.path.length != 0, 147*cc02d7e2SAndroid Build Coastguard Worker @"Neither host nor path can be nil."); 148*cc02d7e2SAndroid Build Coastguard Worker NSAssert(responseHandler != nil, @"Response handler required."); 149*cc02d7e2SAndroid Build Coastguard Worker if (requestOptions.host.length == 0 || requestOptions.path.length == 0) { 150*cc02d7e2SAndroid Build Coastguard Worker return nil; 151*cc02d7e2SAndroid Build Coastguard Worker } 152*cc02d7e2SAndroid Build Coastguard Worker if (responseHandler == nil) { 153*cc02d7e2SAndroid Build Coastguard Worker return nil; 154*cc02d7e2SAndroid Build Coastguard Worker } 155*cc02d7e2SAndroid Build Coastguard Worker 156*cc02d7e2SAndroid Build Coastguard Worker if ((self = [super init])) { 157*cc02d7e2SAndroid Build Coastguard Worker _requestOptions = [requestOptions copy]; 158*cc02d7e2SAndroid Build Coastguard Worker _callOptions = [callOptions copy]; 159*cc02d7e2SAndroid Build Coastguard Worker if (!_callOptions) { 160*cc02d7e2SAndroid Build Coastguard Worker _actualCallOptions = [[GRPCCallOptions alloc] init]; 161*cc02d7e2SAndroid Build Coastguard Worker } else { 162*cc02d7e2SAndroid Build Coastguard Worker _actualCallOptions = [callOptions copy]; 163*cc02d7e2SAndroid Build Coastguard Worker } 164*cc02d7e2SAndroid Build Coastguard Worker _responseHandler = responseHandler; 165*cc02d7e2SAndroid Build Coastguard Worker 166*cc02d7e2SAndroid Build Coastguard Worker GRPCResponseDispatcher *dispatcher = 167*cc02d7e2SAndroid Build Coastguard Worker [[GRPCResponseDispatcher alloc] initWithResponseHandler:_responseHandler]; 168*cc02d7e2SAndroid Build Coastguard Worker NSMutableArray<id<GRPCInterceptorFactory>> *interceptorFactories; 169*cc02d7e2SAndroid Build Coastguard Worker if (_actualCallOptions.interceptorFactories != nil) { 170*cc02d7e2SAndroid Build Coastguard Worker interceptorFactories = 171*cc02d7e2SAndroid Build Coastguard Worker [NSMutableArray arrayWithArray:_actualCallOptions.interceptorFactories]; 172*cc02d7e2SAndroid Build Coastguard Worker } else { 173*cc02d7e2SAndroid Build Coastguard Worker interceptorFactories = [NSMutableArray array]; 174*cc02d7e2SAndroid Build Coastguard Worker } 175*cc02d7e2SAndroid Build Coastguard Worker id<GRPCInterceptorFactory> globalInterceptorFactory = [GRPCCall2 globalInterceptorFactory]; 176*cc02d7e2SAndroid Build Coastguard Worker if (globalInterceptorFactory != nil) { 177*cc02d7e2SAndroid Build Coastguard Worker [interceptorFactories addObject:globalInterceptorFactory]; 178*cc02d7e2SAndroid Build Coastguard Worker } 179*cc02d7e2SAndroid Build Coastguard Worker if (_actualCallOptions.transport != NULL) { 180*cc02d7e2SAndroid Build Coastguard Worker id<GRPCTransportFactory> transportFactory = [[GRPCTransportRegistry sharedInstance] 181*cc02d7e2SAndroid Build Coastguard Worker getTransportFactoryWithID:_actualCallOptions.transport]; 182*cc02d7e2SAndroid Build Coastguard Worker 183*cc02d7e2SAndroid Build Coastguard Worker NSArray<id<GRPCInterceptorFactory>> *transportInterceptorFactories = 184*cc02d7e2SAndroid Build Coastguard Worker transportFactory.transportInterceptorFactories; 185*cc02d7e2SAndroid Build Coastguard Worker if (transportInterceptorFactories != nil) { 186*cc02d7e2SAndroid Build Coastguard Worker [interceptorFactories addObjectsFromArray:transportInterceptorFactories]; 187*cc02d7e2SAndroid Build Coastguard Worker } 188*cc02d7e2SAndroid Build Coastguard Worker } 189*cc02d7e2SAndroid Build Coastguard Worker // continuously create interceptor until one is successfully created 190*cc02d7e2SAndroid Build Coastguard Worker while (_firstInterceptor == nil) { 191*cc02d7e2SAndroid Build Coastguard Worker if (interceptorFactories.count == 0) { 192*cc02d7e2SAndroid Build Coastguard Worker _firstInterceptor = 193*cc02d7e2SAndroid Build Coastguard Worker [[GRPCTransportManager alloc] initWithTransportID:_actualCallOptions.transport 194*cc02d7e2SAndroid Build Coastguard Worker previousInterceptor:dispatcher]; 195*cc02d7e2SAndroid Build Coastguard Worker break; 196*cc02d7e2SAndroid Build Coastguard Worker } else { 197*cc02d7e2SAndroid Build Coastguard Worker _firstInterceptor = 198*cc02d7e2SAndroid Build Coastguard Worker [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories 199*cc02d7e2SAndroid Build Coastguard Worker previousInterceptor:dispatcher 200*cc02d7e2SAndroid Build Coastguard Worker transportID:_actualCallOptions.transport]; 201*cc02d7e2SAndroid Build Coastguard Worker if (_firstInterceptor == nil) { 202*cc02d7e2SAndroid Build Coastguard Worker [interceptorFactories removeObjectAtIndex:0]; 203*cc02d7e2SAndroid Build Coastguard Worker } 204*cc02d7e2SAndroid Build Coastguard Worker } 205*cc02d7e2SAndroid Build Coastguard Worker } 206*cc02d7e2SAndroid Build Coastguard Worker NSAssert(_firstInterceptor != nil, @"Failed to create interceptor or transport."); 207*cc02d7e2SAndroid Build Coastguard Worker if (_firstInterceptor == nil) { 208*cc02d7e2SAndroid Build Coastguard Worker NSLog(@"Failed to create interceptor or transport."); 209*cc02d7e2SAndroid Build Coastguard Worker } 210*cc02d7e2SAndroid Build Coastguard Worker } 211*cc02d7e2SAndroid Build Coastguard Worker return self; 212*cc02d7e2SAndroid Build Coastguard Worker} 213*cc02d7e2SAndroid Build Coastguard Worker 214*cc02d7e2SAndroid Build Coastguard Worker- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions 215*cc02d7e2SAndroid Build Coastguard Worker responseHandler:(id<GRPCResponseHandler>)responseHandler { 216*cc02d7e2SAndroid Build Coastguard Worker return [self initWithRequestOptions:requestOptions 217*cc02d7e2SAndroid Build Coastguard Worker responseHandler:responseHandler 218*cc02d7e2SAndroid Build Coastguard Worker callOptions:nil]; 219*cc02d7e2SAndroid Build Coastguard Worker} 220*cc02d7e2SAndroid Build Coastguard Worker 221*cc02d7e2SAndroid Build Coastguard Worker- (void)start { 222*cc02d7e2SAndroid Build Coastguard Worker id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 223*cc02d7e2SAndroid Build Coastguard Worker GRPCRequestOptions *requestOptions = _requestOptions; 224*cc02d7e2SAndroid Build Coastguard Worker GRPCCallOptions *callOptions = _actualCallOptions; 225*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 226*cc02d7e2SAndroid Build Coastguard Worker [copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; 227*cc02d7e2SAndroid Build Coastguard Worker }); 228*cc02d7e2SAndroid Build Coastguard Worker} 229*cc02d7e2SAndroid Build Coastguard Worker 230*cc02d7e2SAndroid Build Coastguard Worker- (void)cancel { 231*cc02d7e2SAndroid Build Coastguard Worker id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 232*cc02d7e2SAndroid Build Coastguard Worker if (copiedFirstInterceptor != nil) { 233*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 234*cc02d7e2SAndroid Build Coastguard Worker [copiedFirstInterceptor cancel]; 235*cc02d7e2SAndroid Build Coastguard Worker }); 236*cc02d7e2SAndroid Build Coastguard Worker } 237*cc02d7e2SAndroid Build Coastguard Worker} 238*cc02d7e2SAndroid Build Coastguard Worker 239*cc02d7e2SAndroid Build Coastguard Worker- (void)writeData:(id)data { 240*cc02d7e2SAndroid Build Coastguard Worker id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 241*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 242*cc02d7e2SAndroid Build Coastguard Worker [copiedFirstInterceptor writeData:data]; 243*cc02d7e2SAndroid Build Coastguard Worker }); 244*cc02d7e2SAndroid Build Coastguard Worker} 245*cc02d7e2SAndroid Build Coastguard Worker 246*cc02d7e2SAndroid Build Coastguard Worker- (void)finish { 247*cc02d7e2SAndroid Build Coastguard Worker id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 248*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 249*cc02d7e2SAndroid Build Coastguard Worker [copiedFirstInterceptor finish]; 250*cc02d7e2SAndroid Build Coastguard Worker }); 251*cc02d7e2SAndroid Build Coastguard Worker} 252*cc02d7e2SAndroid Build Coastguard Worker 253*cc02d7e2SAndroid Build Coastguard Worker- (void)receiveNextMessages:(NSUInteger)numberOfMessages { 254*cc02d7e2SAndroid Build Coastguard Worker id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 255*cc02d7e2SAndroid Build Coastguard Worker dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 256*cc02d7e2SAndroid Build Coastguard Worker [copiedFirstInterceptor receiveNextMessages:numberOfMessages]; 257*cc02d7e2SAndroid Build Coastguard Worker }); 258*cc02d7e2SAndroid Build Coastguard Worker} 259*cc02d7e2SAndroid Build Coastguard Worker 260*cc02d7e2SAndroid Build Coastguard Worker@end 261