/* * * Copyright 2019 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 #import "GRPCInterceptor.h" #import "private/GRPCTransport+Private.h" @interface GRPCInterceptorManager () @end @implementation GRPCInterceptorManager { id _nextInterceptor; id _previousInterceptor; GRPCInterceptor *_thisInterceptor; dispatch_queue_t _dispatchQueue; NSArray> *_factories; GRPCTransportID _transportID; BOOL _shutDown; } - (instancetype)initWithFactories:(NSArray> *)factories previousInterceptor:(id)previousInterceptor transportID:(nonnull GRPCTransportID)transportID { if ((self = [super init])) { if (factories.count == 0) { [NSException raise:NSInternalInconsistencyException format:@"Interceptor manager must have factories"]; } _thisInterceptor = [factories[0] createInterceptorWithManager:self]; if (_thisInterceptor == nil) { return nil; } _previousInterceptor = previousInterceptor; _factories = factories; // Generate interceptor #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, _thisInterceptor.dispatchQueue); _transportID = transportID; } return self; } - (void)shutDown { // immediately releases reference; should not queue to dispatch queue. _nextInterceptor = nil; _previousInterceptor = nil; _thisInterceptor = nil; _shutDown = YES; } - (void)createNextInterceptor { NSAssert(_nextInterceptor == nil, @"Starting the next interceptor more than once"); NSAssert(_factories.count > 0, @"Interceptor manager of transport cannot start next interceptor"); if (_nextInterceptor != nil) { NSLog(@"Starting the next interceptor more than once"); return; } NSMutableArray> *interceptorFactories = [NSMutableArray arrayWithArray:[_factories subarrayWithRange:NSMakeRange(1, _factories.count - 1)]]; while (_nextInterceptor == nil) { if (interceptorFactories.count == 0) { _nextInterceptor = [[GRPCTransportManager alloc] initWithTransportID:_transportID previousInterceptor:self]; break; } else { _nextInterceptor = [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories previousInterceptor:self transportID:_transportID]; if (_nextInterceptor == nil) { [interceptorFactories removeObjectAtIndex:0]; } } } NSAssert(_nextInterceptor != nil, @"Failed to create interceptor or transport."); if (_nextInterceptor == nil) { NSLog(@"Failed to create interceptor or transport."); } } - (void)startNextInterceptorWithRequest:(GRPCRequestOptions *)requestOptions callOptions:(GRPCCallOptions *)callOptions { if (_nextInterceptor == nil && !_shutDown) { [self createNextInterceptor]; } if (_nextInterceptor == nil) { return; } id copiedNextInterceptor = _nextInterceptor; dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ [copiedNextInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; }); } - (void)writeNextInterceptorWithData:(id)data { if (_nextInterceptor == nil && !_shutDown) { [self createNextInterceptor]; } if (_nextInterceptor == nil) { return; } id copiedNextInterceptor = _nextInterceptor; dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ [copiedNextInterceptor writeData:data]; }); } - (void)finishNextInterceptor { if (_nextInterceptor == nil && !_shutDown) { [self createNextInterceptor]; } if (_nextInterceptor == nil) { return; } id copiedNextInterceptor = _nextInterceptor; dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ [copiedNextInterceptor finish]; }); } - (void)cancelNextInterceptor { if (_nextInterceptor == nil && !_shutDown) { [self createNextInterceptor]; } if (_nextInterceptor == nil) { return; } id copiedNextInterceptor = _nextInterceptor; dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ [copiedNextInterceptor cancel]; }); } /** Notify the next interceptor in the chain to receive more messages */ - (void)receiveNextInterceptorMessages:(NSUInteger)numberOfMessages { if (_nextInterceptor == nil && !_shutDown) { [self createNextInterceptor]; } if (_nextInterceptor == nil) { return; } id copiedNextInterceptor = _nextInterceptor; dispatch_async(copiedNextInterceptor.dispatchQueue, ^{ [copiedNextInterceptor receiveNextMessages:numberOfMessages]; }); } // Methods to forward GRPCResponseHandler callbacks to the previous object /** Forward initial metadata to the previous interceptor in the chain */ - (void)forwardPreviousInterceptorWithInitialMetadata:(NSDictionary *)initialMetadata { if (_previousInterceptor == nil) { return; } id copiedPreviousInterceptor = _previousInterceptor; dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ [copiedPreviousInterceptor didReceiveInitialMetadata:initialMetadata]; }); } /** Forward a received message to the previous interceptor in the chain */ - (void)forwardPreviousInterceptorWithData:(id)data { if (_previousInterceptor == nil) { return; } id copiedPreviousInterceptor = _previousInterceptor; dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ [copiedPreviousInterceptor didReceiveData:data]; }); } /** Forward call close and trailing metadata to the previous interceptor in the chain */ - (void)forwardPreviousInterceptorCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error { if (_previousInterceptor == nil) { return; } id copiedPreviousInterceptor = _previousInterceptor; // no more callbacks should be issued to the previous interceptor _previousInterceptor = nil; dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ [copiedPreviousInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error]; }); } /** Forward write completion to the previous interceptor in the chain */ - (void)forwardPreviousInterceptorDidWriteData { if (_previousInterceptor == nil) { return; } id copiedPreviousInterceptor = _previousInterceptor; dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ [copiedPreviousInterceptor didWriteData]; }); } - (dispatch_queue_t)dispatchQueue { return _dispatchQueue; } - (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions callOptions:(GRPCCallOptions *)callOptions { // retain this interceptor until the method exit to prevent deallocation of the interceptor within // the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor startWithRequestOptions:requestOptions callOptions:callOptions]; } - (void)writeData:(id)data { // retain this interceptor until the method exit to prevent deallocation of the interceptor within // the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor writeData:data]; } - (void)finish { // retain this interceptor until the method exit to prevent deallocation of the interceptor within // the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor finish]; } - (void)cancel { // retain this interceptor until the method exit to prevent deallocation of the interceptor within // the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor cancel]; } - (void)receiveNextMessages:(NSUInteger)numberOfMessages { // retain this interceptor until the method exit to prevent deallocation of the interceptor within // the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor receiveNextMessages:numberOfMessages]; } - (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata { if ([_thisInterceptor respondsToSelector:@selector(didReceiveInitialMetadata:)]) { // retain this interceptor until the method exit to prevent deallocation of the interceptor // within the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor didReceiveInitialMetadata:initialMetadata]; } } - (void)didReceiveData:(id)data { if ([_thisInterceptor respondsToSelector:@selector(didReceiveData:)]) { // retain this interceptor until the method exit to prevent deallocation of the interceptor // within the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor didReceiveData:data]; } } - (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata error:(nullable NSError *)error { if ([_thisInterceptor respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) { // retain this interceptor until the method exit to prevent deallocation of the interceptor // within the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error]; } } - (void)didWriteData { if ([_thisInterceptor respondsToSelector:@selector(didWriteData)]) { // retain this interceptor until the method exit to prevent deallocation of the interceptor // within the interceptor's method GRPCInterceptor *thisInterceptor = _thisInterceptor; [thisInterceptor didWriteData]; } } @end @implementation GRPCInterceptor { GRPCInterceptorManager *_manager; dispatch_queue_t _dispatchQueue; } - (instancetype)initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager dispatchQueue:(dispatch_queue_t)dispatchQueue { if ((self = [super init])) { _manager = interceptorManager; _dispatchQueue = dispatchQueue; } return self; } - (dispatch_queue_t)dispatchQueue { return _dispatchQueue; } - (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions callOptions:(GRPCCallOptions *)callOptions { [_manager startNextInterceptorWithRequest:requestOptions callOptions:callOptions]; } - (void)writeData:(id)data { [_manager writeNextInterceptorWithData:data]; } - (void)finish { [_manager finishNextInterceptor]; } - (void)cancel { [_manager cancelNextInterceptor]; [_manager forwardPreviousInterceptorCloseWithTrailingMetadata:nil error:[NSError errorWithDomain:kGRPCErrorDomain code:GRPCErrorCodeCancelled userInfo:@{ NSLocalizedDescriptionKey : @"Canceled" }]]; [_manager shutDown]; } - (void)receiveNextMessages:(NSUInteger)numberOfMessages { [_manager receiveNextInterceptorMessages:numberOfMessages]; } - (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata { [_manager forwardPreviousInterceptorWithInitialMetadata:initialMetadata]; } - (void)didReceiveRawMessage:(id)message { NSAssert(NO, @"The method didReceiveRawMessage is deprecated and cannot be used with interceptor"); NSLog(@"The method didReceiveRawMessage is deprecated and cannot be used with interceptor"); abort(); } - (void)didReceiveData:(id)data { [_manager forwardPreviousInterceptorWithData:data]; } - (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error { [_manager forwardPreviousInterceptorCloseWithTrailingMetadata:trailingMetadata error:error]; [_manager shutDown]; } - (void)didWriteData { [_manager forwardPreviousInterceptorDidWriteData]; } @end