Merge pull request #2754 from jcanizales/support-test-certificates

Support test certificates and SSL name override in Obj-C
pull/2776/merge
Jorge Canizales 9 years ago
commit a3428f3b4f
  1. 45
      src/objective-c/GRPCClient/GRPCCall+Tests.h
  2. 47
      src/objective-c/GRPCClient/GRPCCall+Tests.m
  3. 12
      src/objective-c/GRPCClient/GRPCCall.m
  4. 13
      src/objective-c/GRPCClient/private/GRPCChannel.h
  5. 56
      src/objective-c/GRPCClient/private/GRPCChannel.m
  6. 2
      src/objective-c/GRPCClient/private/GRPCCompletionQueue.m
  7. 58
      src/objective-c/GRPCClient/private/GRPCHost.h
  8. 134
      src/objective-c/GRPCClient/private/GRPCHost.m
  9. 15
      src/objective-c/GRPCClient/private/GRPCSecureChannel.h
  10. 78
      src/objective-c/GRPCClient/private/GRPCSecureChannel.m
  11. 2
      src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h
  12. 6
      src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m
  13. 7
      src/objective-c/GRPCClient/private/GRPCWrappedCall.h
  14. 36
      src/objective-c/GRPCClient/private/GRPCWrappedCall.m

@ -0,0 +1,45 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "GRPCCall.h"
@interface GRPCCall (Tests)
// Establish all SSL connections to the provided host using the passed SSL target name and the root
// certificates found in the file at |certsPath|.
// Must be called before any gRPC call to that host is made.
+ (void)useTestCertsPath:(NSString *)certsPath
testName:(NSString *)testName
forHost:(NSString *)host;
@end

@ -0,0 +1,47 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "GRPCCall+Tests.h"
#import "private/GRPCHost.h"
@implementation GRPCCall (Tests)
+ (void)useTestCertsPath:(NSString *)certsPath
testName:(NSString *)testName
forHost:(NSString *)host {
GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
hostConfig.secure = YES;
hostConfig.pathToCertificates = certsPath;
hostConfig.hostNameOverride = testName;
}
@end

@ -37,7 +37,6 @@
#include <grpc/support/time.h>
#import <RxLibrary/GRXConcurrentWriteable.h>
#import "private/GRPCChannel.h"
#import "private/GRPCWrappedCall.h"
#import "private/NSData+GRPC.h"
#import "private/NSDictionary+GRPC.h"
@ -70,8 +69,6 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey";
GRPCWrappedCall *_wrappedCall;
dispatch_once_t _callAlreadyInvoked;
GRPCChannel *_channel;
// The C gRPC library has less guarantees on the ordering of events than we
// do. Particularly, in the face of errors, there's no ordering guarantee at
// all. This wrapper over our actual writeable ensures thread-safety and
@ -105,11 +102,10 @@ NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey";
format:@"The requests writer can't be already started."];
}
if ((self = [super init])) {
_channel = [GRPCChannel channelToHost:host];
_wrappedCall = [[GRPCWrappedCall alloc] initWithChannel:_channel
path:path
host:host];
_wrappedCall = [[GRPCWrappedCall alloc] initWithHost:host path:path];
if (!_wrappedCall) {
return nil;
}
// Serial queue to invoke the non-reentrant methods of the grpc_call object.
_callQueue = dispatch_queue_create("org.grpc.call", NULL);

@ -35,17 +35,12 @@
struct grpc_channel;
// Each separate instance of this class represents at least one TCP
// connection to the provided host. To create a grpc_call, pass the
// value of the unmanagedChannel property to grpc_channel_create_call.
// Release this object when the call is finished.
// Each separate instance of this class represents at least one TCP connection to the provided host.
// Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|.
@interface GRPCChannel : NSObject
@property(nonatomic, readonly) struct grpc_channel *unmanagedChannel;
// Convenience constructor to allow for reuse of connections.
+ (instancetype)channelToHost:(NSString *)host;
- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER;
// This initializer takes ownership of the passed channel, and will destroy it when this object is
// deallocated. It's illegal to pass the same grpc_channel to two different GRPCChannel objects.
- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER;
@end

@ -35,53 +35,17 @@
#include <grpc/grpc.h>
#import "GRPCSecureChannel.h"
#import "GRPCUnsecuredChannel.h"
@implementation GRPCChannel
+ (instancetype)channelToHost:(NSString *)host {
// TODO(mlumish): Investigate whether a cache with strong links is a good idea
static NSMutableDictionary *channelCache;
static dispatch_once_t cacheInitialization;
dispatch_once(&cacheInitialization, ^{
channelCache = [NSMutableDictionary dictionary];
});
GRPCChannel *channel = channelCache[host];
if (!channel) {
channel = [[self alloc] initWithHost:host];
channelCache[host] = channel;
}
return channel;
}
- (instancetype)init {
return [self initWithHost:nil];
return [self initWithChannel:NULL];
}
- (instancetype)initWithHost:(NSString *)host {
if (![host rangeOfString:@"://"].length) {
// No scheme provided; assume https.
host = [@"https://" stringByAppendingString:host];
}
NSURL *hostURL = [NSURL URLWithString:host];
if (!hostURL) {
[NSException raise:NSInvalidArgumentException format:@"Invalid URL: %@", host];
// Designated initializer
- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel {
if (!unmanagedChannel) {
return nil;
}
if ([hostURL.scheme isEqualToString:@"https"]) {
host = [@[hostURL.host, hostURL.port ?: @443] componentsJoinedByString:@":"];
return [[GRPCSecureChannel alloc] initWithHost:host];
}
if ([hostURL.scheme isEqualToString:@"http"]) {
host = [@[hostURL.host, hostURL.port ?: @80] componentsJoinedByString:@":"];
return [[GRPCUnsecuredChannel alloc] initWithHost:host];
}
[NSException raise:NSInvalidArgumentException
format:@"URL scheme %@ isn't supported.", hostURL.scheme];
return nil; // silence warning.
}
- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel {
if ((self = [super init])) {
_unmanagedChannel = unmanagedChannel;
}
@ -89,12 +53,8 @@
}
- (void)dealloc {
// _unmanagedChannel is NULL when deallocating an object of the base class (because the
// initializer returns a different object).
if (_unmanagedChannel) {
// TODO(jcanizales): Be sure to add a test with a server that closes the connection prematurely,
// as in the past that made this call to crash.
grpc_channel_destroy(_unmanagedChannel);
}
// TODO(jcanizales): Be sure to add a test with a server that closes the connection prematurely,
// as in the past that made this call to crash.
grpc_channel_destroy(_unmanagedChannel);
}
@end

@ -38,8 +38,6 @@
@implementation GRPCCompletionQueue
+ (instancetype)completionQueue {
// TODO(jcanizales): Reuse completion queues to consume only one thread,
// instead of one per call.
return [[self alloc] init];
}

@ -0,0 +1,58 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import <Foundation/Foundation.h>
@class GRPCCompletionQueue;
struct grpc_call;
@interface GRPCHost : NSObject
@property(nonatomic, readonly) NSString *address;
// The following properties should only be modified for testing:
@property(nonatomic, getter=isSecure) BOOL secure;
@property(nonatomic, copy) NSString *pathToCertificates;
@property(nonatomic, copy) NSString *hostNameOverride;
// Host objects initialized with the same address are the same.
+ (instancetype)hostWithAddress:(NSString *)address;
- (instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER;
// Create a grpc_call object to the provided path on this host.
- (struct grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue;
@end

@ -0,0 +1,134 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#import "GRPCHost.h"
#include <grpc/grpc.h>
#import "GRPCChannel.h"
#import "GRPCCompletionQueue.h"
#import "GRPCSecureChannel.h"
#import "GRPCUnsecuredChannel.h"
@interface GRPCHost ()
// TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
@property(nonatomic, strong) GRPCChannel *channel;
@end
@implementation GRPCHost
+ (instancetype)hostWithAddress:(NSString *)address {
return [[self alloc] initWithAddress:address];
}
- (instancetype)init {
return [self initWithAddress:nil];
}
// Default initializer.
- (instancetype)initWithAddress:(NSString *)address {
// Verify and normalize the address, and decide whether to use SSL.
if (![address rangeOfString:@"://"].length) {
// No scheme provided; assume https.
address = [@"https://" stringByAppendingString:address];
}
NSURL *hostURL = [NSURL URLWithString:address];
if (!hostURL) {
[NSException raise:NSInvalidArgumentException format:@"Invalid URL: %@", address];
}
NSString *scheme = hostURL.scheme;
if (![scheme isEqualToString:@"https"] && ![scheme isEqualToString:@"http"]) {
[NSException raise:NSInvalidArgumentException format:@"URL scheme %@ isn't supported.", scheme];
}
// If the user didn't specify a port (hostURL.port is nil), provide a default one.
NSNumber *port = hostURL.port ?: [scheme isEqualToString:@"https"] ? @443 : @80;
address = [@[hostURL.host, port] componentsJoinedByString:@":"];
// Look up the GRPCHost in the cache.
static NSMutableDictionary *hostCache;
static dispatch_once_t cacheInitialization;
dispatch_once(&cacheInitialization, ^{
hostCache = [NSMutableDictionary dictionary];
});
@synchronized(hostCache) {
GRPCHost *cachedHost = hostCache[address];
if (cachedHost) {
// We could verify here that the cached host uses the same protocol that we're expecting. But
// creating non-SSL channels by adding "http://" to the address is going away (to make the use
// of insecure channels less subtle), so it's not worth it now.
return cachedHost;
}
if ((self = [super init])) {
_address = address;
_secure = [scheme isEqualToString:@"https"];
hostCache[address] = self;
}
return self;
}
}
- (grpc_call *)unmanagedCallWithPath:(NSString *)path completionQueue:(GRPCCompletionQueue *)queue {
if (!queue || !path || !self.channel) {
return NULL;
}
return grpc_channel_create_call(self.channel.unmanagedChannel,
NULL, GRPC_PROPAGATE_DEFAULTS,
queue.unmanagedQueue,
path.UTF8String,
self.hostName.UTF8String,
gpr_inf_future(GPR_CLOCK_REALTIME));
}
- (GRPCChannel *)channel {
// Create it lazily, because we don't want to open a connection just because someone is
// configuring a host.
if (!_channel) {
if (_secure) {
_channel = [[GRPCSecureChannel alloc] initWithHost:_address
pathToCertificates:_pathToCertificates
hostNameOverride:_hostNameOverride];
} else {
_channel = [[GRPCUnsecuredChannel alloc] initWithHost:_address];
}
}
return _channel;
}
- (NSString *)hostName {
// TODO(jcanizales): Default to nil instead of _address when Issue #2635 is clarified.
return _hostNameOverride ?: _address;
}
@end

@ -31,8 +31,23 @@
*
*/
#include <grpc/grpc.h>
#import "GRPCChannel.h"
struct grpc_credentials;
@interface GRPCSecureChannel : GRPCChannel
- (instancetype)initWithHost:(NSString *)host;
// Only in tests shouldn't pathToCertificates or hostNameOverride be nil. Passing nil for
// pathToCertificates results in using the default root certificates distributed with the library.
- (instancetype)initWithHost:(NSString *)host
pathToCertificates:(NSString *)path
hostNameOverride:(NSString *)hostNameOverride;
// The passed arguments aren't required to be valid beyond the invocation of this initializer.
- (instancetype)initWithHost:(NSString *)host
credentials:(struct grpc_credentials *)credentials
args:(grpc_channel_args *)args NS_DESIGNATED_INITIALIZER;
@end

@ -33,28 +33,80 @@
#import "GRPCSecureChannel.h"
#import <grpc/grpc_security.h>
#include <grpc/grpc_security.h>
// Returns NULL if the file at path couldn't be read. In that case, if errorPtr isn't NULL,
// *errorPtr will be an object describing what went wrong.
static grpc_credentials *CertificatesAtPath(NSString *path, NSError **errorPtr) {
NSString *certsContent = [NSString stringWithContentsOfFile:path
encoding:NSASCIIStringEncoding
error:errorPtr];
if (!certsContent) {
// Passing NULL to grpc_ssl_credentials_create produces behavior we don't want, so return.
return NULL;
}
const char * asCString = [certsContent cStringUsingEncoding:NSASCIIStringEncoding];
return grpc_ssl_credentials_create(asCString, NULL);
}
@implementation GRPCSecureChannel
- (instancetype)initWithHost:(NSString *)host {
static grpc_credentials *kCredentials;
return [self initWithHost:host pathToCertificates:nil hostNameOverride:nil];
}
- (instancetype)initWithHost:(NSString *)host
pathToCertificates:(NSString *)path
hostNameOverride:(NSString *)hostNameOverride {
// Load default SSL certificates once.
static grpc_credentials *kDefaultCertificates;
static dispatch_once_t loading;
dispatch_once(&loading, ^{
NSString *defaultPath = @"gRPCCertificates.bundle/roots"; // .pem
// Do not use NSBundle.mainBundle, as it's nil for tests of library projects.
NSBundle *bundle = [NSBundle bundleForClass:self.class];
NSString *certsPath = [bundle pathForResource:@"gRPCCertificates.bundle/roots" ofType:@"pem"];
NSAssert(certsPath.length,
@"gRPCCertificates.bundle/roots.pem not found under %@. This file, with the root "
"certificates, is needed to establish TLS (HTTPS) connections.", bundle.bundlePath);
NSData *certsData = [NSData dataWithContentsOfFile:certsPath];
NSAssert(certsData.length, @"No data read from %@", certsPath);
NSString *certsString = [[NSString alloc] initWithData:certsData encoding:NSUTF8StringEncoding];
kCredentials = grpc_ssl_credentials_create(certsString.UTF8String, NULL);
NSString *path = [bundle pathForResource:defaultPath ofType:@"pem"];
NSError *error;
kDefaultCertificates = CertificatesAtPath(path, &error);
NSAssert(kDefaultCertificates, @"Could not read %@/%@.pem. This file, with the root "
"certificates, is needed to establish secure (TLS) connections. Because the file is "
"distributed with the gRPC library, this error is usually a sign that the library "
"wasn't configured correctly for your project. Error: %@",
bundle.bundlePath, defaultPath, error);
});
return (self = [super initWithChannel:grpc_secure_channel_create(kCredentials,
host.UTF8String,
NULL)]);
//TODO(jcanizales): Add NSError** parameter to the initializer.
grpc_credentials *certificates = path ? CertificatesAtPath(path, NULL) : kDefaultCertificates;
if (!certificates) {
return nil;
}
// Ritual to pass the SSL host name override to the C library.
grpc_channel_args channelArgs;
grpc_arg nameOverrideArg;
channelArgs.num_args = 1;
channelArgs.args = &nameOverrideArg;
nameOverrideArg.type = GRPC_ARG_STRING;
nameOverrideArg.key = GRPC_SSL_TARGET_NAME_OVERRIDE_ARG;
// Cast const away. Hope C gRPC doesn't modify it!
nameOverrideArg.value.string = (char *) hostNameOverride.UTF8String;
grpc_channel_args *args = hostNameOverride ? &channelArgs : NULL;
return [self initWithHost:host credentials:certificates args:args];
}
- (instancetype)initWithHost:(NSString *)host
credentials:(grpc_credentials *)credentials
args:(grpc_channel_args *)args {
return (self =
[super initWithChannel:grpc_secure_channel_create(credentials, host.UTF8String, args)]);
}
// TODO(jcanizales): GRPCSecureChannel and GRPCUnsecuredChannel are just convenience initializers
// for GRPCChannel. Move them into GRPCChannel, which will make the following unnecessary.
- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel {
[NSException raise:NSInternalInconsistencyException format:@"use another initializer"];
return [self initWithHost:nil]; // silence warnings
}
@end

@ -34,5 +34,5 @@
#import "GRPCChannel.h"
@interface GRPCUnsecuredChannel : GRPCChannel
- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER;
@end

@ -41,4 +41,10 @@
return (self = [super initWithChannel:grpc_insecure_channel_create(host.UTF8String, NULL)]);
}
// TODO(jcanizales): GRPCSecureChannel and GRPCUnsecuredChannel are just convenience initializers
// for GRPCChannel. Move them into GRPCChannel, which will make the following unnecessary.
- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel {
[NSException raise:NSInternalInconsistencyException format:@"use the other initializer"];
return [self initWithHost:nil]; // silence warnings
}
@end

@ -81,11 +81,12 @@
@end
#pragma mark GRPCWrappedCall
@interface GRPCWrappedCall : NSObject
- (instancetype)initWithChannel:(GRPCChannel *)channel
path:(NSString *)path
host:(NSString *)host NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithHost:(NSString *)host
path:(NSString *)path NS_DESIGNATED_INITIALIZER;
- (void)startBatchWithOperations:(NSArray *)ops errorHandler:(void(^)())errorHandler;

@ -32,11 +32,14 @@
*/
#import "GRPCWrappedCall.h"
#import <Foundation/Foundation.h>
#include <grpc/grpc.h>
#include <grpc/byte_buffer.h>
#include <grpc/support/alloc.h>
#import "GRPCCompletionQueue.h"
#import "GRPCHost.h"
#import "NSDictionary+GRPC.h"
#import "NSData+GRPC.h"
#import "NSError+GRPC.h"
@ -219,37 +222,36 @@
@end
@implementation GRPCWrappedCall{
grpc_call *_call;
#pragma mark GRPCWrappedCall
@implementation GRPCWrappedCall {
GRPCCompletionQueue *_queue;
grpc_call *_call;
}
- (instancetype)init {
return [self initWithChannel:nil path:nil host:nil];
return [self initWithHost:nil path:nil];
}
- (instancetype)initWithChannel:(GRPCChannel *)channel
path:(NSString *)path
host:(NSString *)host {
if (!channel || !path || !host) {
- (instancetype)initWithHost:(NSString *)host
path:(NSString *)path {
if (!path || !host) {
[NSException raise:NSInvalidArgumentException
format:@"channel, method, and host cannot be nil."];
format:@"path and host cannot be nil."];
}
if (self = [super init]) {
static dispatch_once_t initialization;
dispatch_once(&initialization, ^{
grpc_init();
});
// Each completion queue consumes one thread. There's a trade to be made between creating and
// consuming too many threads and having contention of multiple calls in a single completion
// queue. Currently we favor latency and use one per call.
_queue = [GRPCCompletionQueue completionQueue];
if (!_queue) {
return nil;
}
_call = grpc_channel_create_call(
channel.unmanagedChannel, NULL, GRPC_PROPAGATE_DEFAULTS,
_queue.unmanagedQueue, path.UTF8String, host.UTF8String,
gpr_inf_future(GPR_CLOCK_REALTIME));
_call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path completionQueue:_queue];
if (_call == NULL) {
return nil;
}

Loading…
Cancel
Save