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