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