• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 *
3 * Copyright 2015 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 "GRPCCall.h"
20
21#import "GRPCCall+Interceptor.h"
22#import "GRPCCallOptions.h"
23#import "GRPCInterceptor.h"
24
25#import "GRPCTransport.h"
26#import "private/GRPCTransport+Private.h"
27
28/**
29 * The response dispatcher creates its own serial dispatch queue and target the queue to the
30 * dispatch queue of a user provided response handler. It removes the requirement of having to use
31 * serial dispatch queue in the user provided response handler.
32 */
33@interface GRPCResponseDispatcher : NSObject <GRPCResponseHandler>
34
35- (nullable instancetype)initWithResponseHandler:(id<GRPCResponseHandler>)responseHandler;
36
37@end
38
39@implementation GRPCResponseDispatcher {
40  id<GRPCResponseHandler> _responseHandler;
41  dispatch_queue_t _dispatchQueue;
42}
43
44- (instancetype)initWithResponseHandler:(id<GRPCResponseHandler>)responseHandler {
45  if ((self = [super init])) {
46    _responseHandler = responseHandler;
47#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
48    if (@available(iOS 8.0, macOS 10.10, *)) {
49      _dispatchQueue = dispatch_queue_create(
50          NULL,
51          dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0));
52    } else {
53#else
54    {
55#endif
56      _dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
57    }
58    dispatch_set_target_queue(_dispatchQueue, _responseHandler.dispatchQueue);
59  }
60
61  return self;
62}
63
64- (dispatch_queue_t)dispatchQueue {
65  return _dispatchQueue;
66}
67
68- (void)didReceiveInitialMetadata:(nullable NSDictionary *)initialMetadata {
69  if ([_responseHandler respondsToSelector:@selector(didReceiveInitialMetadata:)]) {
70    [_responseHandler didReceiveInitialMetadata:initialMetadata];
71  }
72}
73
74- (void)didReceiveData:(id)data {
75  // For backwards compatibility with didReceiveRawMessage, if the user provided a response handler
76  // that handles didReceiveRawMesssage, we issue to that method instead
77  if ([_responseHandler respondsToSelector:@selector(didReceiveRawMessage:)]) {
78    [_responseHandler didReceiveRawMessage:data];
79  } else if ([_responseHandler respondsToSelector:@selector(didReceiveData:)]) {
80    [_responseHandler didReceiveData:data];
81  }
82}
83
84- (void)didCloseWithTrailingMetadata:(nullable NSDictionary *)trailingMetadata
85                               error:(nullable NSError *)error {
86  if ([_responseHandler respondsToSelector:@selector(didCloseWithTrailingMetadata:error:)]) {
87    [_responseHandler didCloseWithTrailingMetadata:trailingMetadata error:error];
88  }
89}
90
91- (void)didWriteData {
92  if ([_responseHandler respondsToSelector:@selector(didWriteData)]) {
93    [_responseHandler didWriteData];
94  }
95}
96
97@end
98
99@implementation GRPCRequestOptions
100
101- (instancetype)initWithHost:(NSString *)host path:(NSString *)path safety:(GRPCCallSafety)safety {
102  NSAssert(host.length != 0 && path.length != 0, @"host and path cannot be empty");
103  if (host.length == 0 || path.length == 0) {
104    return nil;
105  }
106  if ((self = [super init])) {
107    _host = [host copy];
108    _path = [path copy];
109    _safety = safety;
110  }
111  return self;
112}
113
114- (id)copyWithZone:(NSZone *)zone {
115  GRPCRequestOptions *request = [[GRPCRequestOptions alloc] initWithHost:_host
116                                                                    path:_path
117                                                                  safety:_safety];
118
119  return request;
120}
121
122@end
123
124/**
125 * This class acts as a wrapper for interceptors
126 */
127@implementation GRPCCall2 {
128  /** The handler of responses. */
129  id<GRPCResponseHandler> _responseHandler;
130
131  /**
132   * Points to the first interceptor in the interceptor chain.
133   */
134  id<GRPCInterceptorInterface> _firstInterceptor;
135
136  /**
137   * The actual call options being used by this call. It is different from the user-provided
138   * call options when the user provided a NULL call options object.
139   */
140  GRPCCallOptions *_actualCallOptions;
141}
142
143- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions
144                       responseHandler:(id<GRPCResponseHandler>)responseHandler
145                           callOptions:(GRPCCallOptions *)callOptions {
146  NSAssert(requestOptions.host.length != 0 && requestOptions.path.length != 0,
147           @"Neither host nor path can be nil.");
148  NSAssert(requestOptions.safety <= GRPCCallSafetyCacheableRequest, @"Invalid call safety value.");
149  NSAssert(responseHandler != nil, @"Response handler required.");
150  if (requestOptions.host.length == 0 || requestOptions.path.length == 0) {
151    return nil;
152  }
153  if (requestOptions.safety > GRPCCallSafetyCacheableRequest) {
154    return nil;
155  }
156  if (responseHandler == nil) {
157    return nil;
158  }
159
160  if ((self = [super init])) {
161    _requestOptions = [requestOptions copy];
162    _callOptions = [callOptions copy];
163    if (!_callOptions) {
164      _actualCallOptions = [[GRPCCallOptions alloc] init];
165    } else {
166      _actualCallOptions = [callOptions copy];
167    }
168    _responseHandler = responseHandler;
169
170    GRPCResponseDispatcher *dispatcher =
171        [[GRPCResponseDispatcher alloc] initWithResponseHandler:_responseHandler];
172    NSMutableArray<id<GRPCInterceptorFactory>> *interceptorFactories;
173    if (_actualCallOptions.interceptorFactories != nil) {
174      interceptorFactories =
175          [NSMutableArray arrayWithArray:_actualCallOptions.interceptorFactories];
176    } else {
177      interceptorFactories = [NSMutableArray array];
178    }
179    id<GRPCInterceptorFactory> globalInterceptorFactory = [GRPCCall2 globalInterceptorFactory];
180    if (globalInterceptorFactory != nil) {
181      [interceptorFactories addObject:globalInterceptorFactory];
182    }
183    if (_actualCallOptions.transport != NULL) {
184      id<GRPCTransportFactory> transportFactory = [[GRPCTransportRegistry sharedInstance]
185          getTransportFactoryWithID:_actualCallOptions.transport];
186
187      NSArray<id<GRPCInterceptorFactory>> *transportInterceptorFactories =
188          transportFactory.transportInterceptorFactories;
189      if (transportInterceptorFactories != nil) {
190        [interceptorFactories addObjectsFromArray:transportInterceptorFactories];
191      }
192    }
193    // continuously create interceptor until one is successfully created
194    while (_firstInterceptor == nil) {
195      if (interceptorFactories.count == 0) {
196        _firstInterceptor =
197            [[GRPCTransportManager alloc] initWithTransportID:_actualCallOptions.transport
198                                          previousInterceptor:dispatcher];
199        break;
200      } else {
201        _firstInterceptor =
202            [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories
203                                          previousInterceptor:dispatcher
204                                                  transportID:_actualCallOptions.transport];
205        if (_firstInterceptor == nil) {
206          [interceptorFactories removeObjectAtIndex:0];
207        }
208      }
209    }
210    NSAssert(_firstInterceptor != nil, @"Failed to create interceptor or transport.");
211    if (_firstInterceptor == nil) {
212      NSLog(@"Failed to create interceptor or transport.");
213    }
214  }
215  return self;
216}
217
218- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions
219                       responseHandler:(id<GRPCResponseHandler>)responseHandler {
220  return [self initWithRequestOptions:requestOptions
221                      responseHandler:responseHandler
222                          callOptions:nil];
223}
224
225- (void)start {
226  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
227  GRPCRequestOptions *requestOptions = _requestOptions;
228  GRPCCallOptions *callOptions = _actualCallOptions;
229  dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
230    [copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions];
231  });
232}
233
234- (void)cancel {
235  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
236  if (copiedFirstInterceptor != nil) {
237    dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
238      [copiedFirstInterceptor cancel];
239    });
240  }
241}
242
243- (void)writeData:(id)data {
244  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
245  dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
246    [copiedFirstInterceptor writeData:data];
247  });
248}
249
250- (void)finish {
251  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
252  dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
253    [copiedFirstInterceptor finish];
254  });
255}
256
257- (void)receiveNextMessages:(NSUInteger)numberOfMessages {
258  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
259  dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
260    [copiedFirstInterceptor receiveNextMessages:numberOfMessages];
261  });
262}
263
264@end
265