• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <Foundation/Foundation.h>
20
21#import "GRPCInterceptor.h"
22#import "private/GRPCTransport+Private.h"
23
24@interface GRPCInterceptorManager () <GRPCInterceptorInterface, GRPCResponseHandler>
25
26@end
27
28@implementation GRPCInterceptorManager {
29  id<GRPCInterceptorInterface> _nextInterceptor;
30  id<GRPCResponseHandler> _previousInterceptor;
31  GRPCInterceptor *_thisInterceptor;
32  dispatch_queue_t _dispatchQueue;
33  NSArray<id<GRPCInterceptorFactory>> *_factories;
34  GRPCTransportID _transportID;
35  BOOL _shutDown;
36}
37
38- (instancetype)initWithFactories:(NSArray<id<GRPCInterceptorFactory>> *)factories
39              previousInterceptor:(id<GRPCResponseHandler>)previousInterceptor
40                      transportID:(nonnull GRPCTransportID)transportID {
41  if ((self = [super init])) {
42    if (factories.count == 0) {
43      [NSException raise:NSInternalInconsistencyException
44                  format:@"Interceptor manager must have factories"];
45    }
46    _thisInterceptor = [factories[0] createInterceptorWithManager:self];
47    if (_thisInterceptor == nil) {
48      return nil;
49    }
50    _previousInterceptor = previousInterceptor;
51    _factories = factories;
52    // Generate interceptor
53#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
54    if (@available(iOS 8.0, macOS 10.10, *)) {
55      _dispatchQueue = dispatch_queue_create(
56          NULL,
57          dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0));
58    } else {
59#else
60    {
61#endif
62      _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
63    }
64    dispatch_set_target_queue(_dispatchQueue, _thisInterceptor.dispatchQueue);
65    _transportID = transportID;
66  }
67  return self;
68}
69
70- (void)shutDown {
71  // immediately releases reference; should not queue to dispatch queue.
72  _nextInterceptor = nil;
73  _previousInterceptor = nil;
74  _thisInterceptor = nil;
75  _shutDown = YES;
76}
77
78- (void)createNextInterceptor {
79  NSAssert(_nextInterceptor == nil, @"Starting the next interceptor more than once");
80  NSAssert(_factories.count > 0, @"Interceptor manager of transport cannot start next interceptor");
81  if (_nextInterceptor != nil) {
82    NSLog(@"Starting the next interceptor more than once");
83    return;
84  }
85  NSMutableArray<id<GRPCInterceptorFactory>> *interceptorFactories = [NSMutableArray
86      arrayWithArray:[_factories subarrayWithRange:NSMakeRange(1, _factories.count - 1)]];
87  while (_nextInterceptor == nil) {
88    if (interceptorFactories.count == 0) {
89      _nextInterceptor = [[GRPCTransportManager alloc] initWithTransportID:_transportID
90                                                       previousInterceptor:self];
91      break;
92    } else {
93      _nextInterceptor = [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories
94                                                       previousInterceptor:self
95                                                               transportID:_transportID];
96      if (_nextInterceptor == nil) {
97        [interceptorFactories removeObjectAtIndex:0];
98      }
99    }
100  }
101  NSAssert(_nextInterceptor != nil, @"Failed to create interceptor or transport.");
102  if (_nextInterceptor == nil) {
103    NSLog(@"Failed to create interceptor or transport.");
104  }
105}
106
107- (void)startNextInterceptorWithRequest:(GRPCRequestOptions *)requestOptions
108                            callOptions:(GRPCCallOptions *)callOptions {
109  if (_nextInterceptor == nil && !_shutDown) {
110    [self createNextInterceptor];
111  }
112  if (_nextInterceptor == nil) {
113    return;
114  }
115  id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
116  dispatch_async(copiedNextInterceptor.dispatchQueue, ^{
117    [copiedNextInterceptor startWithRequestOptions:requestOptions callOptions:callOptions];
118  });
119}
120
121- (void)writeNextInterceptorWithData:(id)data {
122  if (_nextInterceptor == nil && !_shutDown) {
123    [self createNextInterceptor];
124  }
125  if (_nextInterceptor == nil) {
126    return;
127  }
128  id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
129  dispatch_async(copiedNextInterceptor.dispatchQueue, ^{
130    [copiedNextInterceptor writeData:data];
131  });
132}
133
134- (void)finishNextInterceptor {
135  if (_nextInterceptor == nil && !_shutDown) {
136    [self createNextInterceptor];
137  }
138  if (_nextInterceptor == nil) {
139    return;
140  }
141  id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
142  dispatch_async(copiedNextInterceptor.dispatchQueue, ^{
143    [copiedNextInterceptor finish];
144  });
145}
146
147- (void)cancelNextInterceptor {
148  if (_nextInterceptor == nil && !_shutDown) {
149    [self createNextInterceptor];
150  }
151  if (_nextInterceptor == nil) {
152    return;
153  }
154  id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
155  dispatch_async(copiedNextInterceptor.dispatchQueue, ^{
156    [copiedNextInterceptor cancel];
157  });
158}
159
160/** Notify the next interceptor in the chain to receive more messages */
161- (void)receiveNextInterceptorMessages:(NSUInteger)numberOfMessages {
162  if (_nextInterceptor == nil && !_shutDown) {
163    [self createNextInterceptor];
164  }
165  if (_nextInterceptor == nil) {
166    return;
167  }
168  id<GRPCInterceptorInterface> copiedNextInterceptor = _nextInterceptor;
169  dispatch_async(copiedNextInterceptor.dispatchQueue, ^{
170    [copiedNextInterceptor receiveNextMessages:numberOfMessages];
171  });
172}
173
174// Methods to forward GRPCResponseHandler callbacks to the previous object
175
176/** Forward initial metadata to the previous interceptor in the chain */
177- (void)forwardPreviousInterceptorWithInitialMetadata:(NSDictionary *)initialMetadata {
178  if (_previousInterceptor == nil) {
179    return;
180  }
181  id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor;
182  dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{
183    [copiedPreviousInterceptor didReceiveInitialMetadata:initialMetadata];
184  });
185}
186
187/** Forward a received message to the previous interceptor in the chain */
188- (void)forwardPreviousInterceptorWithData:(id)data {
189  if (_previousInterceptor == nil) {
190    return;
191  }
192  id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor;
193  dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{
194    [copiedPreviousInterceptor didReceiveData:data];
195  });
196}
197
198/** Forward call close and trailing metadata to the previous interceptor in the chain */
199- (void)forwardPreviousInterceptorCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata
200                                                      error:(NSError *)error {
201  if (_previousInterceptor == nil) {
202    return;
203  }
204  id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor;
205  // no more callbacks should be issued to the previous interceptor
206  _previousInterceptor = nil;
207  dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{
208    [copiedPreviousInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error];
209  });
210}
211
212/** Forward write completion to the previous interceptor in the chain */
213- (void)forwardPreviousInterceptorDidWriteData {
214  if (_previousInterceptor == nil) {
215    return;
216  }
217  id<GRPCResponseHandler> copiedPreviousInterceptor = _previousInterceptor;
218  dispatch_async(copiedPreviousInterceptor.dispatchQueue, ^{
219    [copiedPreviousInterceptor didWriteData];
220  });
221}
222
223- (dispatch_queue_t)dispatchQueue {
224  return _dispatchQueue;
225}
226
227- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions
228                    callOptions:(GRPCCallOptions *)callOptions {
229  // retain this interceptor until the method exit to prevent deallocation of the interceptor within
230  // the interceptor's method
231  GRPCInterceptor *thisInterceptor = _thisInterceptor;
232  [thisInterceptor startWithRequestOptions:requestOptions callOptions:callOptions];
233}
234
235- (void)writeData:(id)data {
236  // retain this interceptor until the method exit to prevent deallocation of the interceptor within
237  // the interceptor's method
238  GRPCInterceptor *thisInterceptor = _thisInterceptor;
239  [thisInterceptor writeData:data];
240}
241
242- (void)finish {
243  // retain this interceptor until the method exit to prevent deallocation of the interceptor within
244  // the interceptor's method
245  GRPCInterceptor *thisInterceptor = _thisInterceptor;
246  [thisInterceptor finish];
247}
248
249- (void)cancel {
250  // retain this interceptor until the method exit to prevent deallocation of the interceptor within
251  // the interceptor's method
252  GRPCInterceptor *thisInterceptor = _thisInterceptor;
253  [thisInterceptor cancel];
254}
255
256- (void)receiveNextMessages:(NSUInteger)numberOfMessages {
257  // retain this interceptor until the method exit to prevent deallocation of the interceptor within
258  // the interceptor's method
259  GRPCInterceptor *thisInterceptor = _thisInterceptor;
260  [thisInterceptor receiveNextMessages:numberOfMessages];
261}
262
263- (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata {
264  if ([_thisInterceptor respondsToSelector:@selector(didReceiveInitialMetadata:)]) {
265    // retain this interceptor until the method exit to prevent deallocation of the interceptor
266    // within the interceptor's method
267    GRPCInterceptor *thisInterceptor = _thisInterceptor;
268    [thisInterceptor didReceiveInitialMetadata:initialMetadata];
269  }
270}
271
272- (void)didReceiveData:(id)data {
273  if ([_thisInterceptor respondsToSelector:@selector(didReceiveData:)]) {
274    // retain this interceptor until the method exit to prevent deallocation of the interceptor
275    // within the interceptor's method
276    GRPCInterceptor *thisInterceptor = _thisInterceptor;
277    [thisInterceptor didReceiveData:data];
278  }
279}
280
281- (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata
282                               error:(nullable NSError *)error {
283  if ([_thisInterceptor respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) {
284    // retain this interceptor until the method exit to prevent deallocation of the interceptor
285    // within the interceptor's method
286    GRPCInterceptor *thisInterceptor = _thisInterceptor;
287    [thisInterceptor didCloseWithTrailingMetadata:trailingMetadata error:error];
288  }
289}
290
291- (void)didWriteData {
292  if ([_thisInterceptor respondsToSelector:@selector(didWriteData)]) {
293    // retain this interceptor until the method exit to prevent deallocation of the interceptor
294    // within the interceptor's method
295    GRPCInterceptor *thisInterceptor = _thisInterceptor;
296    [thisInterceptor didWriteData];
297  }
298}
299
300@end
301
302@implementation GRPCInterceptor {
303  GRPCInterceptorManager *_manager;
304  dispatch_queue_t _dispatchQueue;
305}
306
307- (instancetype)initWithInterceptorManager:(GRPCInterceptorManager *)interceptorManager
308                             dispatchQueue:(dispatch_queue_t)dispatchQueue {
309  if ((self = [super init])) {
310    _manager = interceptorManager;
311    _dispatchQueue = dispatchQueue;
312  }
313
314  return self;
315}
316
317- (dispatch_queue_t)dispatchQueue {
318  return _dispatchQueue;
319}
320
321- (void)startWithRequestOptions:(GRPCRequestOptions *)requestOptions
322                    callOptions:(GRPCCallOptions *)callOptions {
323  [_manager startNextInterceptorWithRequest:requestOptions callOptions:callOptions];
324}
325
326- (void)writeData:(id)data {
327  [_manager writeNextInterceptorWithData:data];
328}
329
330- (void)finish {
331  [_manager finishNextInterceptor];
332}
333
334- (void)cancel {
335  [_manager cancelNextInterceptor];
336  [_manager
337      forwardPreviousInterceptorCloseWithTrailingMetadata:nil
338                                                    error:[NSError
339                                                              errorWithDomain:kGRPCErrorDomain
340                                                                         code:GRPCErrorCodeCancelled
341                                                                     userInfo:@{
342                                                                       NSLocalizedDescriptionKey :
343                                                                           @"Canceled"
344                                                                     }]];
345  [_manager shutDown];
346}
347
348- (void)receiveNextMessages:(NSUInteger)numberOfMessages {
349  [_manager receiveNextInterceptorMessages:numberOfMessages];
350}
351
352- (void)didReceiveInitialMetadata:(NSDictionary *)initialMetadata {
353  [_manager forwardPreviousInterceptorWithInitialMetadata:initialMetadata];
354}
355
356- (void)didReceiveRawMessage:(id)message {
357  NSAssert(NO,
358           @"The method didReceiveRawMessage is deprecated and cannot be used with interceptor");
359  NSLog(@"The method didReceiveRawMessage is deprecated and cannot be used with interceptor");
360  abort();
361}
362
363- (void)didReceiveData:(id)data {
364  [_manager forwardPreviousInterceptorWithData:data];
365}
366
367- (void)didCloseWithTrailingMetadata:(NSDictionary *)trailingMetadata error:(NSError *)error {
368  [_manager forwardPreviousInterceptorCloseWithTrailingMetadata:trailingMetadata error:error];
369  [_manager shutDown];
370}
371
372- (void)didWriteData {
373  [_manager forwardPreviousInterceptorDidWriteData];
374}
375
376@end
377