/* * * Copyright 2015 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #import "GRPCCall.h" #import "GRPCCall+Interceptor.h" #import "GRPCCallOptions.h" #import "GRPCInterceptor.h" #import "GRPCTransport.h" #import "private/GRPCTransport+Private.h" /** * The response dispatcher creates its own serial dispatch queue and target the queue to the * dispatch queue of a user provided response handler. It removes the requirement of having to use * serial dispatch queue in the user provided response handler. */ @interface GRPCResponseDispatcher : NSObject - (nullable instancetype)initWithResponseHandler:(id)responseHandler; @end @implementation GRPCResponseDispatcher { id _responseHandler; dispatch_queue_t _dispatchQueue; } - (instancetype)initWithResponseHandler:(id)responseHandler { if ((self = [super init])) { _responseHandler = responseHandler; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 if (@available(iOS 8.0, macOS 10.10, *)) { _dispatchQueue = dispatch_queue_create( NULL, dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0)); } else { #else { #endif _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); } dispatch_set_target_queue(_dispatchQueue, _responseHandler.dispatchQueue); } return self; } - (dispatch_queue_t)dispatchQueue { return _dispatchQueue; } - (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata { if ([_responseHandler respondsToSelector:@selector(didReceiveInitialMetadata:)]) { [_responseHandler didReceiveInitialMetadata:initialMetadata]; } } - (void)didReceiveData:(id)data { // For backwards compatibility with didReceiveRawMessage, if the user provided a response handler // that handles didReceiveRawMesssage, we issue to that method instead if ([_responseHandler respondsToSelector:@selector(didReceiveRawMessage:)]) { [_responseHandler didReceiveRawMessage:data]; } else if ([_responseHandler respondsToSelector:@selector(didReceiveData:)]) { [_responseHandler didReceiveData:data]; } } - (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata error:(nullable NSError *)error { if ([_responseHandler respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { [_responseHandler didCloseWithTrailingMetadata:trailingMetadata error:error]; } } - (void)didWriteData { if ([_responseHandler respondsToSelector:@selector(didWriteData)]) { [_responseHandler didWriteData]; } } @end @implementation GRPCRequestOptions - (instancetype)initWithHost:(NSString *)host path:(NSString *)path safety:(GRPCCallSafety)safety { NSAssert(host.length != 0 && path.length != 0, @"host and path cannot be empty"); if (host.length == 0 || path.length == 0) { return nil; } if ((self = [super init])) { _host = [host copy]; _path = [path copy]; _safety = safety; } return self; } - (id)copyWithZone:(NSZone *)zone { GRPCRequestOptions *request = [[GRPCRequestOptions alloc] initWithHost:_host path:_path safety:_safety]; return request; } @end /** * This class acts as a wrapper for interceptors */ @implementation GRPCCall2 { /** The handler of responses. */ id _responseHandler; /** * Points to the first interceptor in the interceptor chain. */ id _firstInterceptor; /** * The actual call options being used by this call. It is different from the user-provided * call options when the user provided a NULL call options object. */ GRPCCallOptions *_actualCallOptions; } - (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions responseHandler:(id)responseHandler callOptions:(GRPCCallOptions *)callOptions { NSAssert(requestOptions.host.length != 0 && requestOptions.path.length != 0, @"Neither host nor path can be nil."); NSAssert(responseHandler != nil, @"Response handler required."); if (requestOptions.host.length == 0 || requestOptions.path.length == 0) { return nil; } if (responseHandler == nil) { return nil; } if ((self = [super init])) { _requestOptions = [requestOptions copy]; _callOptions = [callOptions copy]; if (!_callOptions) { _actualCallOptions = [[GRPCCallOptions alloc] init]; } else { _actualCallOptions = [callOptions copy]; } _responseHandler = responseHandler; GRPCResponseDispatcher *dispatcher = [[GRPCResponseDispatcher alloc] initWithResponseHandler:_responseHandler]; NSMutableArray> *interceptorFactories; if (_actualCallOptions.interceptorFactories != nil) { interceptorFactories = [NSMutableArray arrayWithArray:_actualCallOptions.interceptorFactories]; } else { interceptorFactories = [NSMutableArray array]; } id globalInterceptorFactory = [GRPCCall2 globalInterceptorFactory]; if (globalInterceptorFactory != nil) { [interceptorFactories addObject:globalInterceptorFactory]; } if (_actualCallOptions.transport != NULL) { id transportFactory = [[GRPCTransportRegistry sharedInstance] getTransportFactoryWithID:_actualCallOptions.transport]; NSArray> *transportInterceptorFactories = transportFactory.transportInterceptorFactories; if (transportInterceptorFactories != nil) { [interceptorFactories addObjectsFromArray:transportInterceptorFactories]; } } // continuously create interceptor until one is successfully created while (_firstInterceptor == nil) { if (interceptorFactories.count == 0) { _firstInterceptor = [[GRPCTransportManager alloc] initWithTransportID:_actualCallOptions.transport previousInterceptor:dispatcher]; break; } else { _firstInterceptor = [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories previousInterceptor:dispatcher transportID:_actualCallOptions.transport]; if (_firstInterceptor == nil) { [interceptorFactories removeObjectAtIndex:0]; } } } NSAssert(_firstInterceptor != nil, @"Failed to create interceptor or transport."); if (_firstInterceptor == nil) { NSLog(@"Failed to create interceptor or transport."); } } return self; } - (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions responseHandler:(id)responseHandler { return [self initWithRequestOptions:requestOptions responseHandler:responseHandler callOptions:nil]; } - (void)start { id copiedFirstInterceptor = _firstInterceptor; GRPCRequestOptions *requestOptions = _requestOptions; GRPCCallOptions *callOptions = _actualCallOptions; dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ [copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; }); } - (void)cancel { id copiedFirstInterceptor = _firstInterceptor; if (copiedFirstInterceptor != nil) { dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ [copiedFirstInterceptor cancel]; }); } } - (void)writeData:(id)data { id copiedFirstInterceptor = _firstInterceptor; dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ [copiedFirstInterceptor writeData:data]; }); } - (void)finish { id copiedFirstInterceptor = _firstInterceptor; dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ [copiedFirstInterceptor finish]; }); } - (void)receiveNextMessages:(NSUInteger)numberOfMessages { id copiedFirstInterceptor = _firstInterceptor; dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ [copiedFirstInterceptor receiveNextMessages:numberOfMessages]; }); } @end