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(requestOptions.safety <= GRPCCallSafetyCacheableRequest, @"Invalid call safety value."); 149 NSAssert(responseHandler != nil, @"Response handler required."); 150 if (requestOptions.host.length == 0 || requestOptions.path.length == 0) { 151 return nil; 152 } 153 if (requestOptions.safety > GRPCCallSafetyCacheableRequest) { 154 return nil; 155 } 156 if (responseHandler == nil) { 157 return nil; 158 } 159 160 if ((self = [super init])) { 161 _requestOptions = [requestOptions copy]; 162 _callOptions = [callOptions copy]; 163 if (!_callOptions) { 164 _actualCallOptions = [[GRPCCallOptions alloc] init]; 165 } else { 166 _actualCallOptions = [callOptions copy]; 167 } 168 _responseHandler = responseHandler; 169 170 GRPCResponseDispatcher *dispatcher = 171 [[GRPCResponseDispatcher alloc] initWithResponseHandler:_responseHandler]; 172 NSMutableArray<id<GRPCInterceptorFactory>> *interceptorFactories; 173 if (_actualCallOptions.interceptorFactories != nil) { 174 interceptorFactories = 175 [NSMutableArray arrayWithArray:_actualCallOptions.interceptorFactories]; 176 } else { 177 interceptorFactories = [NSMutableArray array]; 178 } 179 id<GRPCInterceptorFactory> globalInterceptorFactory = [GRPCCall2 globalInterceptorFactory]; 180 if (globalInterceptorFactory != nil) { 181 [interceptorFactories addObject:globalInterceptorFactory]; 182 } 183 if (_actualCallOptions.transport != NULL) { 184 id<GRPCTransportFactory> transportFactory = [[GRPCTransportRegistry sharedInstance] 185 getTransportFactoryWithID:_actualCallOptions.transport]; 186 187 NSArray<id<GRPCInterceptorFactory>> *transportInterceptorFactories = 188 transportFactory.transportInterceptorFactories; 189 if (transportInterceptorFactories != nil) { 190 [interceptorFactories addObjectsFromArray:transportInterceptorFactories]; 191 } 192 } 193 // continuously create interceptor until one is successfully created 194 while (_firstInterceptor == nil) { 195 if (interceptorFactories.count == 0) { 196 _firstInterceptor = 197 [[GRPCTransportManager alloc] initWithTransportID:_actualCallOptions.transport 198 previousInterceptor:dispatcher]; 199 break; 200 } else { 201 _firstInterceptor = 202 [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories 203 previousInterceptor:dispatcher 204 transportID:_actualCallOptions.transport]; 205 if (_firstInterceptor == nil) { 206 [interceptorFactories removeObjectAtIndex:0]; 207 } 208 } 209 } 210 NSAssert(_firstInterceptor != nil, @"Failed to create interceptor or transport."); 211 if (_firstInterceptor == nil) { 212 NSLog(@"Failed to create interceptor or transport."); 213 } 214 } 215 return self; 216} 217 218- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions 219 responseHandler:(id<GRPCResponseHandler>)responseHandler { 220 return [self initWithRequestOptions:requestOptions 221 responseHandler:responseHandler 222 callOptions:nil]; 223} 224 225- (void)start { 226 id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 227 GRPCRequestOptions *requestOptions = _requestOptions; 228 GRPCCallOptions *callOptions = _actualCallOptions; 229 dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 230 [copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; 231 }); 232} 233 234- (void)cancel { 235 id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 236 if (copiedFirstInterceptor != nil) { 237 dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 238 [copiedFirstInterceptor cancel]; 239 }); 240 } 241} 242 243- (void)writeData:(id)data { 244 id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 245 dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 246 [copiedFirstInterceptor writeData:data]; 247 }); 248} 249 250- (void)finish { 251 id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 252 dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 253 [copiedFirstInterceptor finish]; 254 }); 255} 256 257- (void)receiveNextMessages:(NSUInteger)numberOfMessages { 258 id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; 259 dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ 260 [copiedFirstInterceptor receiveNextMessages:numberOfMessages]; 261 }); 262} 263 264@end 265