xref: /aosp_15_r20/external/grpc-grpc/src/objective-c/GRPCClient/GRPCCall.mm (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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(responseHandler != nil, @"Response handler required.");
149  if (requestOptions.host.length == 0 || requestOptions.path.length == 0) {
150    return nil;
151  }
152  if (responseHandler == nil) {
153    return nil;
154  }
155
156  if ((self = [super init])) {
157    _requestOptions = [requestOptions copy];
158    _callOptions = [callOptions copy];
159    if (!_callOptions) {
160      _actualCallOptions = [[GRPCCallOptions alloc] init];
161    } else {
162      _actualCallOptions = [callOptions copy];
163    }
164    _responseHandler = responseHandler;
165
166    GRPCResponseDispatcher *dispatcher =
167        [[GRPCResponseDispatcher alloc] initWithResponseHandler:_responseHandler];
168    NSMutableArray<id<GRPCInterceptorFactory>> *interceptorFactories;
169    if (_actualCallOptions.interceptorFactories != nil) {
170      interceptorFactories =
171          [NSMutableArray arrayWithArray:_actualCallOptions.interceptorFactories];
172    } else {
173      interceptorFactories = [NSMutableArray array];
174    }
175    id<GRPCInterceptorFactory> globalInterceptorFactory = [GRPCCall2 globalInterceptorFactory];
176    if (globalInterceptorFactory != nil) {
177      [interceptorFactories addObject:globalInterceptorFactory];
178    }
179    if (_actualCallOptions.transport != NULL) {
180      id<GRPCTransportFactory> transportFactory = [[GRPCTransportRegistry sharedInstance]
181          getTransportFactoryWithID:_actualCallOptions.transport];
182
183      NSArray<id<GRPCInterceptorFactory>> *transportInterceptorFactories =
184          transportFactory.transportInterceptorFactories;
185      if (transportInterceptorFactories != nil) {
186        [interceptorFactories addObjectsFromArray:transportInterceptorFactories];
187      }
188    }
189    // continuously create interceptor until one is successfully created
190    while (_firstInterceptor == nil) {
191      if (interceptorFactories.count == 0) {
192        _firstInterceptor =
193            [[GRPCTransportManager alloc] initWithTransportID:_actualCallOptions.transport
194                                          previousInterceptor:dispatcher];
195        break;
196      } else {
197        _firstInterceptor =
198            [[GRPCInterceptorManager alloc] initWithFactories:interceptorFactories
199                                          previousInterceptor:dispatcher
200                                                  transportID:_actualCallOptions.transport];
201        if (_firstInterceptor == nil) {
202          [interceptorFactories removeObjectAtIndex:0];
203        }
204      }
205    }
206    NSAssert(_firstInterceptor != nil, @"Failed to create interceptor or transport.");
207    if (_firstInterceptor == nil) {
208      NSLog(@"Failed to create interceptor or transport.");
209    }
210  }
211  return self;
212}
213
214- (instancetype)initWithRequestOptions:(GRPCRequestOptions *)requestOptions
215                       responseHandler:(id<GRPCResponseHandler>)responseHandler {
216  return [self initWithRequestOptions:requestOptions
217                      responseHandler:responseHandler
218                          callOptions:nil];
219}
220
221- (void)start {
222  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
223  GRPCRequestOptions *requestOptions = _requestOptions;
224  GRPCCallOptions *callOptions = _actualCallOptions;
225  dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
226    [copiedFirstInterceptor startWithRequestOptions:requestOptions callOptions:callOptions];
227  });
228}
229
230- (void)cancel {
231  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
232  if (copiedFirstInterceptor != nil) {
233    dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
234      [copiedFirstInterceptor cancel];
235    });
236  }
237}
238
239- (void)writeData:(id)data {
240  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
241  dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
242    [copiedFirstInterceptor writeData:data];
243  });
244}
245
246- (void)finish {
247  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
248  dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
249    [copiedFirstInterceptor finish];
250  });
251}
252
253- (void)receiveNextMessages:(NSUInteger)numberOfMessages {
254  id<GRPCInterceptorInterface> copiedFirstInterceptor = _firstInterceptor;
255  dispatch_async(copiedFirstInterceptor.dispatchQueue, ^{
256    [copiedFirstInterceptor receiveNextMessages:numberOfMessages];
257  });
258}
259
260@end
261