xref: /aosp_15_r20/external/grpc-grpc/src/objective-c/GRPCClient/private/GRPCCore/GRPCWrappedCall.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 "GRPCWrappedCall.h"
20
21#import <Foundation/Foundation.h>
22#include <grpc/byte_buffer.h>
23#include <grpc/grpc.h>
24#include <grpc/support/alloc.h>
25
26#import "GRPCChannel.h"
27#import "GRPCChannelPool.h"
28#import "GRPCCompletionQueue.h"
29#import "GRPCHost.h"
30#import "NSData+GRPC.h"
31#import "NSDictionary+GRPC.h"
32#import "NSError+GRPC.h"
33
34#import "GRPCOpBatchLog.h"
35
36@implementation GRPCOperation {
37 @protected
38  // Most operation subclasses don't set any flags in the grpc_op, and rely on the flag member being
39  // initialized to zero.
40  grpc_op _op;
41  void (^_handler)(void);
42}
43
44- (void)finish {
45  if (_handler) {
46    void (^handler)(void) = _handler;
47    _handler = nil;
48    handler();
49  }
50}
51@end
52
53@implementation GRPCOpSendMetadata
54
55- (instancetype)init {
56  return [self initWithMetadata:nil flags:0 handler:nil];
57}
58
59- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)(void))handler {
60  return [self initWithMetadata:metadata flags:0 handler:handler];
61}
62
63- (instancetype)initWithMetadata:(NSDictionary *)metadata
64                           flags:(uint32_t)flags
65                         handler:(void (^)(void))handler {
66  if (self = [super init]) {
67    _op.op = GRPC_OP_SEND_INITIAL_METADATA;
68    _op.data.send_initial_metadata.count = metadata.count;
69    _op.data.send_initial_metadata.metadata = metadata.grpc_metadataArray;
70    _op.data.send_initial_metadata.maybe_compression_level.is_set = false;
71    _op.data.send_initial_metadata.maybe_compression_level.level = GRPC_COMPRESS_LEVEL_NONE;
72    _op.flags = flags;
73    _handler = handler;
74  }
75  return self;
76}
77
78- (void)dealloc {
79  for (int i = 0; i < _op.data.send_initial_metadata.count; i++) {
80    grpc_slice_unref(_op.data.send_initial_metadata.metadata[i].key);
81    grpc_slice_unref(_op.data.send_initial_metadata.metadata[i].value);
82  }
83  gpr_free(_op.data.send_initial_metadata.metadata);
84}
85
86@end
87
88@implementation GRPCOpSendMessage
89
90- (instancetype)init {
91  return [self initWithMessage:nil handler:nil];
92}
93
94- (instancetype)initWithMessage:(NSData *)message handler:(void (^)(void))handler {
95  if (!message) {
96    [NSException raise:NSInvalidArgumentException format:@"message cannot be nil"];
97  }
98  if (self = [super init]) {
99    _op.op = GRPC_OP_SEND_MESSAGE;
100    _op.data.send_message.send_message = message.grpc_byteBuffer;
101    _handler = handler;
102  }
103  return self;
104}
105
106- (void)dealloc {
107  grpc_byte_buffer_destroy(_op.data.send_message.send_message);
108}
109
110@end
111
112@implementation GRPCOpSendClose
113
114- (instancetype)init {
115  return [self initWithHandler:nil];
116}
117
118- (instancetype)initWithHandler:(void (^)(void))handler {
119  if (self = [super init]) {
120    _op.op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
121    _handler = handler;
122  }
123  return self;
124}
125
126@end
127
128@implementation GRPCOpRecvMetadata {
129  grpc_metadata_array _headers;
130}
131
132- (instancetype)init {
133  return [self initWithHandler:nil];
134}
135
136- (instancetype)initWithHandler:(void (^)(NSDictionary *))handler {
137  if (self = [super init]) {
138    _op.op = GRPC_OP_RECV_INITIAL_METADATA;
139    grpc_metadata_array_init(&_headers);
140    _op.data.recv_initial_metadata.recv_initial_metadata = &_headers;
141    if (handler) {
142      // Prevent reference cycle with _handler
143      __weak auto weakSelf = self;
144      _handler = ^{
145        __strong auto strongSelf = weakSelf;
146        if (strongSelf) {
147          NSDictionary *metadata =
148              [NSDictionary grpc_dictionaryFromMetadataArray:strongSelf->_headers];
149          handler(metadata);
150        }
151      };
152    }
153  }
154  return self;
155}
156
157- (void)dealloc {
158  grpc_metadata_array_destroy(&_headers);
159}
160
161@end
162
163@implementation GRPCOpRecvMessage {
164  grpc_byte_buffer *_receivedMessage;
165}
166
167- (instancetype)init {
168  return [self initWithHandler:nil];
169}
170
171- (instancetype)initWithHandler:(void (^)(grpc_byte_buffer *))handler {
172  if (self = [super init]) {
173    _op.op = GRPC_OP_RECV_MESSAGE;
174    _op.data.recv_message.recv_message = &_receivedMessage;
175    if (handler) {
176      // Prevent reference cycle with _handler
177      __weak auto weakSelf = self;
178      _handler = ^{
179        __strong auto strongSelf = weakSelf;
180        if (strongSelf) {
181          handler(strongSelf->_receivedMessage);
182        }
183      };
184    }
185  }
186  return self;
187}
188
189@end
190
191@implementation GRPCOpRecvStatus {
192  grpc_status_code _statusCode;
193  grpc_slice _details;
194  size_t _detailsCapacity;
195  grpc_metadata_array _trailers;
196  const char *_errorString;
197}
198
199- (instancetype)init {
200  return [self initWithHandler:nil];
201}
202
203- (instancetype)initWithHandler:(void (^)(NSError *, NSDictionary *))handler {
204  if (self = [super init]) {
205    _op.op = GRPC_OP_RECV_STATUS_ON_CLIENT;
206    _op.data.recv_status_on_client.status = &_statusCode;
207    _op.data.recv_status_on_client.status_details = &_details;
208    grpc_metadata_array_init(&_trailers);
209    _op.data.recv_status_on_client.trailing_metadata = &_trailers;
210    _op.data.recv_status_on_client.error_string = &_errorString;
211    if (handler) {
212      // Prevent reference cycle with _handler
213      __weak auto weakSelf = self;
214      _handler = ^{
215        __strong auto strongSelf = weakSelf;
216        if (strongSelf) {
217          char *details = grpc_slice_to_c_string(strongSelf->_details);
218          NSError *error = [NSError grpc_errorFromStatusCode:strongSelf->_statusCode
219                                                     details:details
220                                                 errorString:strongSelf->_errorString];
221          NSDictionary *trailers =
222              [NSDictionary grpc_dictionaryFromMetadataArray:strongSelf->_trailers];
223          handler(error, trailers);
224          gpr_free(details);
225        }
226      };
227    }
228  }
229  return self;
230}
231
232- (void)dealloc {
233  grpc_metadata_array_destroy(&_trailers);
234  grpc_slice_unref(_details);
235  gpr_free((void *)_errorString);
236}
237
238@end
239
240#pragma mark GRPCWrappedCall
241
242@implementation GRPCWrappedCall {
243  // pooledChannel holds weak reference to this object so this is ok
244  GRPCPooledChannel *_pooledChannel;
245  grpc_call *_call;
246}
247
248- (instancetype)initWithUnmanagedCall:(grpc_call *)unmanagedCall
249                        pooledChannel:(GRPCPooledChannel *)pooledChannel {
250  NSAssert(unmanagedCall != NULL, @"unmanagedCall cannot be empty.");
251  NSAssert(pooledChannel != nil, @"pooledChannel cannot be empty.");
252  if (unmanagedCall == NULL || pooledChannel == nil) {
253    return nil;
254  }
255
256  if ((self = [super init])) {
257    _call = unmanagedCall;
258    _pooledChannel = pooledChannel;
259  }
260  return self;
261}
262
263- (void)startBatchWithOperations:(NSArray *)operations {
264  [self startBatchWithOperations:operations errorHandler:nil];
265}
266
267- (void)startBatchWithOperations:(NSArray *)operations errorHandler:(void (^)(void))errorHandler {
268// Keep logs of op batches when we are running tests. Disabled when in production for improved
269// performance.
270#ifdef GRPC_TEST_OBJC
271  [GRPCOpBatchLog addOpBatchToLog:operations];
272#endif
273
274  @synchronized(self) {
275    if (_call != NULL) {
276      size_t nops = operations.count;
277      grpc_op *ops_array = (grpc_op *)gpr_malloc(nops * sizeof(grpc_op));
278      size_t i = 0;
279      for (GRPCOperation *operation in operations) {
280        ops_array[i++] = operation.op;
281      }
282      grpc_call_error error =
283          grpc_call_start_batch(_call, ops_array, nops, (__bridge_retained void *)(^(bool success) {
284                                  if (!success) {
285                                    if (errorHandler) {
286                                      errorHandler();
287                                    } else {
288                                      return;
289                                    }
290                                  }
291                                  for (GRPCOperation *operation in operations) {
292                                    [operation finish];
293                                  }
294                                }),
295                                NULL);
296      gpr_free(ops_array);
297
298      NSAssert(error == GRPC_CALL_OK, @"Error starting a batch of operations: %i", error);
299      // To avoid compiler complaint when NSAssert is disabled.
300      if (error != GRPC_CALL_OK) {
301        return;
302      }
303    }
304  }
305}
306
307- (void)cancel {
308  @synchronized(self) {
309    if (_call != NULL) {
310      grpc_call_cancel(_call, NULL);
311    }
312  }
313}
314
315- (void)channelDisconnected {
316  @synchronized(self) {
317    if (_call != NULL) {
318      // Unreference the call will lead to its cancellation in the core. Note that since
319      // this function is only called with a network state change, any existing GRPCCall object will
320      // also receive the same notification and cancel themselves with GRPCErrorCodeUnavailable, so
321      // the user gets GRPCErrorCodeUnavailable in this case.
322      grpc_call_unref(_call);
323      _call = NULL;
324    }
325  }
326}
327
328- (void)dealloc {
329  @synchronized(self) {
330    if (_call != NULL) {
331      grpc_call_unref(_call);
332      _call = NULL;
333    }
334  }
335  // Explicitly converting weak reference _pooledChannel to strong.
336  __strong GRPCPooledChannel *channel = _pooledChannel;
337  [channel notifyWrappedCallDealloc:self];
338}
339
340@end
341