| /* |
| * |
| * 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 <GRPCResponseHandler> |
| |
| - (nullable instancetype)initWithResponseHandler:(id<GRPCResponseHandler>)responseHandler; |
| |
| @end |
| |
| @implementation GRPCResponseDispatcher { |
| id<GRPCResponseHandler> _responseHandler; |
| dispatch_queue_t _dispatchQueue; |
| } |
| |
| - (instancetype)initWithResponseHandler:(id<GRPCResponseHandler>)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<GRPCResponseHandler> _responseHandler; |
| |
| /** |
| * Points to the first interceptor in the interceptor chain. |
| */ |
| id<GRPCInterceptorInterface> _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<GRPCResponseHandler>)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<id<GRPCInterceptorFactory>> *interceptorFactories; |
| if (_actualCallOptions.interceptorFactories != nil) { |
| interceptorFactories = |
| [NSMutableArray arrayWithArray:_actualCallOptions.interceptorFactories]; |
| } else { |
| interceptorFactories = [NSMutableArray array]; |
| } |
| id<GRPCInterceptorFactory> globalInterceptorFactory = [GRPCCall2 globalInterceptorFactory]; |
| if (globalInterceptorFactory != nil) { |
| [interceptorFactories addObject:globalInterceptorFactory]; |
| } |
| if (_actualCallOptions.transport != NULL) { |
| id<GRPCTransportFactory> transportFactory = [[GRPCTransportRegistry sharedInstance] |
| getTransportFactoryWithID:_actualCallOptions.transport]; |
| |
| NSArray<id<GRPCInterceptorFactory>> *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<GRPCResponseHandler>)responseHandler { |
| return [self initWithRequestOptions:requestOptions |
| responseHandler:responseHandler |
| callOptions:nil]; |
| } |
| |
| - (void)start { |
| id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; |
| GRPCRequestOptions *requestOptions = _requestOptions; |
| GRPCCallOptions *callOptions = _actualCallOptions; |
| dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ |
| [copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; |
| }); |
| } |
| |
| - (void)cancel { |
| id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; |
| if (copiedFirstInterceptor != nil) { |
| dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ |
| [copiedFirstInterceptor cancel]; |
| }); |
| } |
| } |
| |
| - (void)writeData:(id)data { |
| id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; |
| dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ |
| [copiedFirstInterceptor writeData:data]; |
| }); |
| } |
| |
| - (void)finish { |
| id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; |
| dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ |
| [copiedFirstInterceptor finish]; |
| }); |
| } |
| |
| - (void)receiveNextMessages:(NSUInteger)numberOfMessages { |
| id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor; |
| dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{ |
| [copiedFirstInterceptor receiveNextMessages:numberOfMessages]; |
| }); |
| } |
| |
| @end |