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