• 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 "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 = 0;
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 typeof(self) weakSelf = self;
144      _handler = ^{
145        __strong typeof(self) strongSelf = weakSelf;
146        NSDictionary *metadata =
147            [NSDictionary grpc_dictionaryFromMetadataArray:strongSelf->_headers];
148        handler(metadata);
149      };
150    }
151  }
152  return self;
153}
154
155- (void)dealloc {
156  grpc_metadata_array_destroy(&_headers);
157}
158
159@end
160
161@implementation GRPCOpRecvMessage {
162  grpc_byte_buffer *_receivedMessage;
163}
164
165- (instancetype)init {
166  return [self initWithHandler:nil];
167}
168
169- (instancetype)initWithHandler:(void (^)(grpc_byte_buffer *))handler {
170  if (self = [super init]) {
171    _op.op = GRPC_OP_RECV_MESSAGE;
172    _op.data.recv_message.recv_message = &_receivedMessage;
173    if (handler) {
174      // Prevent reference cycle with _handler
175      __weak typeof(self) weakSelf = self;
176      _handler = ^{
177        __strong typeof(self) strongSelf = weakSelf;
178        handler(strongSelf->_receivedMessage);
179      };
180    }
181  }
182  return self;
183}
184
185@end
186
187@implementation GRPCOpRecvStatus {
188  grpc_status_code _statusCode;
189  grpc_slice _details;
190  size_t _detailsCapacity;
191  grpc_metadata_array _trailers;
192  const char *_errorString;
193}
194
195- (instancetype)init {
196  return [self initWithHandler:nil];
197}
198
199- (instancetype)initWithHandler:(void (^)(NSError *, NSDictionary *))handler {
200  if (self = [super init]) {
201    _op.op = GRPC_OP_RECV_STATUS_ON_CLIENT;
202    _op.data.recv_status_on_client.status = &_statusCode;
203    _op.data.recv_status_on_client.status_details = &_details;
204    grpc_metadata_array_init(&_trailers);
205    _op.data.recv_status_on_client.trailing_metadata = &_trailers;
206    _op.data.recv_status_on_client.error_string = &_errorString;
207    if (handler) {
208      // Prevent reference cycle with _handler
209      __weak typeof(self) weakSelf = self;
210      _handler = ^{
211        __strong typeof(self) strongSelf = weakSelf;
212        if (strongSelf) {
213          char *details = grpc_slice_to_c_string(strongSelf->_details);
214          NSError *error = [NSError grpc_errorFromStatusCode:strongSelf->_statusCode
215                                                     details:details
216                                                 errorString:strongSelf->_errorString];
217          NSDictionary *trailers =
218              [NSDictionary grpc_dictionaryFromMetadataArray:strongSelf->_trailers];
219          handler(error, trailers);
220          gpr_free(details);
221        }
222      };
223    }
224  }
225  return self;
226}
227
228- (void)dealloc {
229  grpc_metadata_array_destroy(&_trailers);
230  grpc_slice_unref(_details);
231  gpr_free((void *)_errorString);
232}
233
234@end
235
236#pragma mark GRPCWrappedCall
237
238@implementation GRPCWrappedCall {
239  // pooledChannel holds weak reference to this object so this is ok
240  GRPCPooledChannel *_pooledChannel;
241  grpc_call *_call;
242}
243
244- (instancetype)initWithUnmanagedCall:(grpc_call *)unmanagedCall
245                        pooledChannel:(GRPCPooledChannel *)pooledChannel {
246  NSAssert(unmanagedCall != NULL, @"unmanagedCall cannot be empty.");
247  NSAssert(pooledChannel != nil, @"pooledChannel cannot be empty.");
248  if (unmanagedCall == NULL || pooledChannel == nil) {
249    return nil;
250  }
251
252  if ((self = [super init])) {
253    _call = unmanagedCall;
254    _pooledChannel = pooledChannel;
255  }
256  return self;
257}
258
259- (void)startBatchWithOperations:(NSArray *)operations {
260  [self startBatchWithOperations:operations errorHandler:nil];
261}
262
263- (void)startBatchWithOperations:(NSArray *)operations errorHandler:(void (^)(void))errorHandler {
264// Keep logs of op batches when we are running tests. Disabled when in production for improved
265// performance.
266#ifdef GRPC_TEST_OBJC
267  [GRPCOpBatchLog addOpBatchToLog:operations];
268#endif
269
270  @synchronized(self) {
271    if (_call != NULL) {
272      size_t nops = operations.count;
273      grpc_op *ops_array = gpr_malloc(nops * sizeof(grpc_op));
274      size_t i = 0;
275      for (GRPCOperation *operation in operations) {
276        ops_array[i++] = operation.op;
277      }
278      grpc_call_error error =
279          grpc_call_start_batch(_call, ops_array, nops, (__bridge_retained void *)(^(bool success) {
280                                  if (!success) {
281                                    if (errorHandler) {
282                                      errorHandler();
283                                    } else {
284                                      return;
285                                    }
286                                  }
287                                  for (GRPCOperation *operation in operations) {
288                                    [operation finish];
289                                  }
290                                }),
291                                NULL);
292      gpr_free(ops_array);
293
294      NSAssert(error == GRPC_CALL_OK, @"Error starting a batch of operations: %i", error);
295      // To avoid compiler complaint when NSAssert is disabled.
296      if (error != GRPC_CALL_OK) {
297        return;
298      }
299    }
300  }
301}
302
303- (void)cancel {
304  @synchronized(self) {
305    if (_call != NULL) {
306      grpc_call_cancel(_call, NULL);
307    }
308  }
309}
310
311- (void)channelDisconnected {
312  @synchronized(self) {
313    if (_call != NULL) {
314      // Unreference the call will lead to its cancellation in the core. Note that since
315      // this function is only called with a network state change, any existing GRPCCall object will
316      // also receive the same notification and cancel themselves with GRPCErrorCodeUnavailable, so
317      // the user gets GRPCErrorCodeUnavailable in this case.
318      grpc_call_unref(_call);
319      _call = NULL;
320    }
321  }
322}
323
324- (void)dealloc {
325  @synchronized(self) {
326    if (_call != NULL) {
327      grpc_call_unref(_call);
328      _call = NULL;
329    }
330  }
331  // Explicitly converting weak reference _pooledChannel to strong.
332  __strong GRPCPooledChannel *channel = _pooledChannel;
333  [channel notifyWrappedCallDealloc:self];
334}
335
336@end
337