1/* 2 * 3 * Copyright 2019 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 "GRPCTransport+Private.h" 20 21#import <GRPCClient/GRPCTransport.h> 22 23@implementation GRPCTransportManager { 24 GRPCTransportID _transportID; 25 GRPCTransport *_transport; 26 id<GRPCResponseHandler> _previousInterceptor; 27 dispatch_queue_t _dispatchQueue; 28} 29 30- (instancetype)initWithTransportID:(GRPCTransportID)transportID 31 previousInterceptor:(id<GRPCResponseHandler>)previousInterceptor { 32 if ((self = [super init])) { 33 id<GRPCTransportFactory> factory = 34 [[GRPCTransportRegistry sharedInstance] getTransportFactoryWithID:transportID]; 35 36 _transport = [factory createTransportWithManager:self]; 37 NSAssert(_transport != nil, @"Failed to create transport with id: %s", transportID); 38 if (_transport == nil) { 39 NSLog(@"Failed to create transport with id: %s", transportID); 40 return nil; 41 } 42 _previousInterceptor = previousInterceptor; 43 _dispatchQueue = _transport.dispatchQueue; 44 _transportID = transportID; 45 } 46 return self; 47} 48 49- (void)shutDown { 50 // immediately releases reference; should not queue to dispatch queue. 51 _transport = nil; 52 _previousInterceptor = nil; 53} 54 55- (dispatch_queue_t)dispatchQueue { 56 return _dispatchQueue; 57} 58 59- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions 60 callOptions:(GRPCCallOptions *)callOptions { 61 if (_transportID != callOptions.transport) { 62 [NSException raise:NSInvalidArgumentException 63 format:@"Interceptors cannot change the call option 'transport'"]; 64 return; 65 } 66 // retain the transport instance until the method exit to prevent deallocation of the transport 67 // instance within the method 68 GRPCTransport *transport = _transport; 69 [transport startWithRequestOptions:requestOptions callOptions:callOptions]; 70} 71 72- (void)writeData:(id)data { 73 // retain the transport instance until the method exit to prevent deallocation of the transport 74 // instance within the method 75 GRPCTransport *transport = _transport; 76 [transport writeData:data]; 77} 78 79- (void)finish { 80 // retain the transport instance until the method exit to prevent deallocation of the transport 81 // instance within the method 82 GRPCTransport *transport = _transport; 83 [transport finish]; 84} 85 86- (void)cancel { 87 // retain the transport instance until the method exit to prevent deallocation of the transport 88 // instance within the method 89 GRPCTransport *transport = _transport; 90 [transport cancel]; 91} 92 93- (void)receiveNextMessages:(NSUInteger)numberOfMessages { 94 // retain the transport instance until the method exit to prevent deallocation of the transport 95 // instance within the method 96 GRPCTransport *transport = _transport; 97 [transport receiveNextMessages:numberOfMessages]; 98} 99 100/** Forward initial metadata to the previous interceptor in the chain */ 101- (void)forwardPreviousInterceptorWithInitialMetadata:(NSDictionary *)initialMetadata { 102 if (initialMetadata == nil || _previousInterceptor == nil) { 103 return; 104 } 105 id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; 106 dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ 107 [copiedPreviousInterceptor didReceiveInitialMetadata:initialMetadata]; 108 }); 109} 110 111/** Forward a received message to the previous interceptor in the chain */ 112- (void)forwardPreviousInterceptorWithData:(id)data { 113 if (data == nil || _previousInterceptor == nil) { 114 return; 115 } 116 id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; 117 dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ 118 [copiedPreviousInterceptor didReceiveData:data]; 119 }); 120} 121 122/** Forward call close and trailing metadata to the previous interceptor in the chain */ 123- (void)forwardPreviousInterceptorCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata 124 error:(NSError *)error { 125 if (_previousInterceptor == nil) { 126 return; 127 } 128 id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; 129 // no more callbacks should be issued to the previous interceptor 130 _previousInterceptor = nil; 131 dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ 132 [copiedPreviousInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error]; 133 }); 134} 135 136/** Forward write completion to the previous interceptor in the chain */ 137- (void)forwardPreviousInterceptorDidWriteData { 138 if (_previousInterceptor == nil) { 139 return; 140 } 141 id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor; 142 dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{ 143 [copiedPreviousInterceptor didWriteData]; 144 }); 145} 146 147@end 148