Home | History | Annotate | Download | only in private
      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