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 "GRPCCompletionQueue.h" 27#import "GRPCHost.h" 28#import "NSData+GRPC.h" 29#import "NSDictionary+GRPC.h" 30#import "NSError+GRPC.h" 31 32#import "GRPCOpBatchLog.h" 33 34@implementation GRPCOperation { 35 @protected 36 // Most operation subclasses don't set any flags in the grpc_op, and rely on the flag member being 37 // initialized to zero. 38 grpc_op _op; 39 void (^_handler)(void); 40} 41 42- (void)finish { 43 if (_handler) { 44 void (^handler)(void) = _handler; 45 _handler = nil; 46 handler(); 47 } 48} 49@end 50 51@implementation GRPCOpSendMetadata 52 53- (instancetype)init { 54 return [self initWithMetadata:nil flags:0 handler:nil]; 55} 56 57- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)(void))handler { 58 return [self initWithMetadata:metadata flags:0 handler:handler]; 59} 60 61- (instancetype)initWithMetadata:(NSDictionary *)metadata 62 flags:(uint32_t)flags 63 handler:(void (^)(void))handler { 64 if (self = [super init]) { 65 _op.op = GRPC_OP_SEND_INITIAL_METADATA; 66 _op.data.send_initial_metadata.count = metadata.count; 67 _op.data.send_initial_metadata.metadata = metadata.grpc_metadataArray; 68 _op.data.send_initial_metadata.maybe_compression_level.is_set = false; 69 _op.data.send_initial_metadata.maybe_compression_level.level = 0; 70 _op.flags = flags; 71 _handler = handler; 72 } 73 return self; 74} 75 76- (void)dealloc { 77 for (int i = 0; i < _op.data.send_initial_metadata.count; i++) { 78 grpc_slice_unref(_op.data.send_initial_metadata.metadata[i].key); 79 grpc_slice_unref(_op.data.send_initial_metadata.metadata[i].value); 80 } 81 gpr_free(_op.data.send_initial_metadata.metadata); 82} 83 84@end 85 86@implementation GRPCOpSendMessage 87 88- (instancetype)init { 89 return [self initWithMessage:nil handler:nil]; 90} 91 92- (instancetype)initWithMessage:(NSData *)message handler:(void (^)(void))handler { 93 if (!message) { 94 [NSException raise:NSInvalidArgumentException format:@"message cannot be nil"]; 95 } 96 if (self = [super init]) { 97 _op.op = GRPC_OP_SEND_MESSAGE; 98 _op.data.send_message.send_message = message.grpc_byteBuffer; 99 _handler = handler; 100 } 101 return self; 102} 103 104- (void)dealloc { 105 grpc_byte_buffer_destroy(_op.data.send_message.send_message); 106} 107 108@end 109 110@implementation GRPCOpSendClose 111 112- (instancetype)init { 113 return [self initWithHandler:nil]; 114} 115 116- (instancetype)initWithHandler:(void (^)(void))handler { 117 if (self = [super init]) { 118 _op.op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; 119 _handler = handler; 120 } 121 return self; 122} 123 124@end 125 126@implementation GRPCOpRecvMetadata { 127 grpc_metadata_array _headers; 128} 129 130- (instancetype)init { 131 return [self initWithHandler:nil]; 132} 133 134- (instancetype)initWithHandler:(void (^)(NSDictionary *))handler { 135 if (self = [super init]) { 136 _op.op = GRPC_OP_RECV_INITIAL_METADATA; 137 grpc_metadata_array_init(&_headers); 138 _op.data.recv_initial_metadata.recv_initial_metadata = &_headers; 139 if (handler) { 140 // Prevent reference cycle with _handler 141 __weak typeof(self) weakSelf = self; 142 _handler = ^{ 143 __strong typeof(self) strongSelf = weakSelf; 144 NSDictionary *metadata = 145 [NSDictionary grpc_dictionaryFromMetadataArray:strongSelf->_headers]; 146 handler(metadata); 147 }; 148 } 149 } 150 return self; 151} 152 153- (void)dealloc { 154 grpc_metadata_array_destroy(&_headers); 155} 156 157@end 158 159@implementation GRPCOpRecvMessage { 160 grpc_byte_buffer *_receivedMessage; 161} 162 163- (instancetype)init { 164 return [self initWithHandler:nil]; 165} 166 167- (instancetype)initWithHandler:(void (^)(grpc_byte_buffer *))handler { 168 if (self = [super init]) { 169 _op.op = GRPC_OP_RECV_MESSAGE; 170 _op.data.recv_message.recv_message = &_receivedMessage; 171 if (handler) { 172 // Prevent reference cycle with _handler 173 __weak typeof(self) weakSelf = self; 174 _handler = ^{ 175 __strong typeof(self) strongSelf = weakSelf; 176 handler(strongSelf->_receivedMessage); 177 }; 178 } 179 } 180 return self; 181} 182 183@end 184 185@implementation GRPCOpRecvStatus { 186 grpc_status_code _statusCode; 187 grpc_slice _details; 188 size_t _detailsCapacity; 189 grpc_metadata_array _trailers; 190 const char *_errorString; 191} 192 193- (instancetype)init { 194 return [self initWithHandler:nil]; 195} 196 197- (instancetype)initWithHandler:(void (^)(NSError *, NSDictionary *))handler { 198 if (self = [super init]) { 199 _op.op = GRPC_OP_RECV_STATUS_ON_CLIENT; 200 _op.data.recv_status_on_client.status = &_statusCode; 201 _op.data.recv_status_on_client.status_details = &_details; 202 grpc_metadata_array_init(&_trailers); 203 _op.data.recv_status_on_client.trailing_metadata = &_trailers; 204 _op.data.recv_status_on_client.error_string = &_errorString; 205 if (handler) { 206 // Prevent reference cycle with _handler 207 __weak typeof(self) weakSelf = self; 208 _handler = ^{ 209 __strong typeof(self) strongSelf = weakSelf; 210 if (strongSelf) { 211 char *details = grpc_slice_to_c_string(strongSelf->_details); 212 NSError *error = [NSError grpc_errorFromStatusCode:strongSelf->_statusCode 213 details:details 214 errorString:strongSelf->_errorString]; 215 NSDictionary *trailers = 216 [NSDictionary grpc_dictionaryFromMetadataArray:strongSelf->_trailers]; 217 handler(error, trailers); 218 gpr_free(details); 219 } 220 }; 221 } 222 } 223 return self; 224} 225 226- (void)dealloc { 227 grpc_metadata_array_destroy(&_trailers); 228 grpc_slice_unref(_details); 229 gpr_free((void *)_errorString); 230} 231 232@end 233 234#pragma mark GRPCWrappedCall 235 236@implementation GRPCWrappedCall { 237 GRPCCompletionQueue *_queue; 238 grpc_call *_call; 239} 240 241- (instancetype)init { 242 return [self initWithHost:nil serverName:nil path:nil timeout:0]; 243} 244 245- (instancetype)initWithHost:(NSString *)host 246 serverName:(NSString *)serverName 247 path:(NSString *)path 248 timeout:(NSTimeInterval)timeout { 249 if (!path || !host) { 250 [NSException raise:NSInvalidArgumentException format:@"path and host cannot be nil."]; 251 } 252 253 if (self = [super init]) { 254 // Each completion queue consumes one thread. There's a trade to be made between creating and 255 // consuming too many threads and having contention of multiple calls in a single completion 256 // queue. Currently we use a singleton queue. 257 _queue = [GRPCCompletionQueue completionQueue]; 258 259 _call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path 260 serverName:serverName 261 timeout:timeout 262 completionQueue:_queue]; 263 if (_call == NULL) { 264 return nil; 265 } 266 } 267 return self; 268} 269 270- (void)startBatchWithOperations:(NSArray *)operations { 271 [self startBatchWithOperations:operations errorHandler:nil]; 272} 273 274- (void)startBatchWithOperations:(NSArray *)operations errorHandler:(void (^)(void))errorHandler { 275// Keep logs of op batches when we are running tests. Disabled when in production for improved 276// performance. 277#ifdef GRPC_TEST_OBJC 278 [GRPCOpBatchLog addOpBatchToLog:operations]; 279#endif 280 281 size_t nops = operations.count; 282 grpc_op *ops_array = gpr_malloc(nops * sizeof(grpc_op)); 283 size_t i = 0; 284 for (GRPCOperation *operation in operations) { 285 ops_array[i++] = operation.op; 286 } 287 grpc_call_error error = 288 grpc_call_start_batch(_call, ops_array, nops, (__bridge_retained void *)(^(bool success) { 289 if (!success) { 290 if (errorHandler) { 291 errorHandler(); 292 } else { 293 return; 294 } 295 } 296 for (GRPCOperation *operation in operations) { 297 [operation finish]; 298 } 299 }), 300 NULL); 301 gpr_free(ops_array); 302 303 if (error != GRPC_CALL_OK) { 304 [NSException 305 raise:NSInternalInconsistencyException 306 format:@"A precondition for calling grpc_call_start_batch wasn't met. Error %i", error]; 307 } 308} 309 310- (void)cancel { 311 grpc_call_cancel(_call, NULL); 312} 313 314- (void)dealloc { 315 grpc_call_unref(_call); 316} 317 318@end 319