diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h new file mode 100644 index 0000000000000000000000000000000000000000..3d617b05d99df786e451589adaf96bbc7a45b922 --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.h @@ -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 diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.m b/src/objective-c/GRPCClient/GRPCCall+Tests.m new file mode 100644 index 0000000000000000000000000000000000000000..7c5b81d661d756b9cbb654285a6145d0bf7608ba --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.m @@ -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 diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h index 49f8b712b9d09aed061178d118342f6c006d97f8..fa9f6f28d1f541437dd540f3f86d7e991934448b 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -36,15 +36,9 @@ #include <grpc/grpc.h> // Each separate instance of this class represents at least one TCP connection to the provided host. -// To create a grpc_call to that host, use |unmanagedCallWithPath|. Release this object when the -// call is finished. +// Create them using one of the subclasses |GRPCSecureChannel| and |GRPCUnsecuredChannel|. @interface GRPCChannel : NSObject +@property(nonatomic) grpc_channel *unmanagedChannel; -// Convenience constructor to allow for reuse of connections. -+ (instancetype)channelToHost:(NSString *)host; - -- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel - hostName:(NSString *)hostName NS_DESIGNATED_INITIALIZER; - -- (grpc_call *)unmanagedCallWithPath:(NSString *)path; +- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m index 90973cd83d09f9e082273672bcec9aa499a2838a..68a402db97a4865823b3ad02a926a996c8cbc968 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCChannel.m @@ -33,85 +33,23 @@ #import "GRPCChannel.h" -#include <grpc/grpc.h> - -#import "GRPCCompletionQueue.h" -#import "GRPCSecureChannel.h" -#import "GRPCUnsecuredChannel.h" - -@implementation GRPCChannel { - grpc_channel *_unmanagedChannel; - NSString *_hostName; - GRPCCompletionQueue *_queue; -} - -+ (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 uncachedChannelToHost:host]; - channelCache[host] = channel; - } - return channel; -} - -+ (instancetype)uncachedChannelToHost:(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]; - } - 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. -} +@implementation GRPCChannel - (instancetype)init { - return [self initWithChannel:NULL hostName:nil]; + return [self initWithChannel:NULL]; } -- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel - hostName:(NSString *)hostName { - if (!unmanagedChannel || !hostName) { - [NSException raise:NSInvalidArgumentException - format:@"Neither unmanagedChannel nor hostName can be nil."]; +// Designated initializer +- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel { + if (!unmanagedChannel) { + [NSException raise:NSInvalidArgumentException format:@"unmanagedChannel can't be nil."]; } if ((self = [super init])) { _unmanagedChannel = unmanagedChannel; - _hostName = hostName; - // In case sharing the queue creates contention, we can change it to one per grpc_call. - _queue = [GRPCCompletionQueue completionQueue]; - if (!_queue) { - return nil; - } } return self; } -- (grpc_call *)unmanagedCallWithPath:(NSString *)path { - return grpc_channel_create_call(_unmanagedChannel, - _queue.unmanagedQueue, - path.UTF8String, - _hostName.UTF8String, - gpr_inf_future(GPR_CLOCK_REALTIME)); -} - - (void)dealloc { // 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. diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m index 12535c961653cc85e34440ebf3d7fb71469fde5f..696069c200f503c8be40b49e193d488b5c56fec6 100644 --- a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m +++ b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.m @@ -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]; } diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h new file mode 100644 index 0000000000000000000000000000000000000000..86e43a283d7ea69ef08b24704bcb5e022ff6d675 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCHost.h @@ -0,0 +1,56 @@ +/* + * + * 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> + +#import "GRPCCompletionQueue.h" + +@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. +- (grpc_call *)unmanagedCallWithPath:(NSString *)path completionQueue:(GRPCCompletionQueue *)queue; + +@end diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m new file mode 100644 index 0000000000000000000000000000000000000000..405545b86bb0b787c375b288ff0266243d412aee --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCHost.m @@ -0,0 +1,126 @@ +/* + * + * 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" + +#import "GRPCChannel.h" +#import "GRPCSecureChannel.h" +#import "GRPCUnsecuredChannel.h" + +@interface GRPCHost () +// TODO(mlumish): Investigate whether a 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]; + } + NSNumber *port = hostURL.port ?: [scheme isEqualToString:@"https"] ? @443 : @80; + address = [@[hostURL.host, port] componentsJoinedByString:@":"]; + + // Look up the GRPCHost in the cache. + // TODO(jcanizales): Make this cache thread-safe. + static NSMutableDictionary *hostCache; + static dispatch_once_t cacheInitialization; + dispatch_once(&cacheInitialization, ^{ + hostCache = [NSMutableDictionary dictionary]; + }); + if (hostCache[address]) { + // We could verify here that the cached host uses the same protocol that we're expecting. But + // picking HTTP by adding the scheme to the address is going away (to make the use of insecure + // channels less subtle), so it's not worth it now. + return hostCache[address]; + } + + 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) { + return NULL; + } + return grpc_channel_create_call(self.channel.unmanagedChannel, + queue.unmanagedQueue, + path.UTF8String, + self.hostName.UTF8String, + gpr_inf_future(GPR_CLOCK_REALTIME)); +} + +- (GRPCChannel *)channel { + // Create it lazily. + 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 diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h index 8c5fe33d61e4618b01fafcbdeb3bfc92120a38a2..8b259c8dadaf3a5d0bfaf1bf736a6515d15ff14e 100644 --- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h @@ -31,8 +31,18 @@ * */ +#import <grpc/grpc_security.h> + #import "GRPCChannel.h" @interface GRPCSecureChannel : GRPCChannel -- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithHost:(NSString *)host; + +- (instancetype)initWithHost:(NSString *)host + pathToCertificates:(NSString *)path + hostNameOverride:(NSString *)hostNameOverride; + +- (instancetype)initWithHost:(NSString *)host + credentials:(grpc_credentials *)credentials + args:(grpc_channel_args *)args NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m index 2dbbd52087837b6e17373ea6cbc320ba9d612b31..c783462dd3056e26df887772784c33d162b33532 100644 --- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m @@ -33,12 +33,24 @@ #import "GRPCSecureChannel.h" -#import <grpc/grpc_security.h> +grpc_credentials *CertificatesAtPath(NSString *path) { + NSData *certsData = [NSData dataWithContentsOfFile:path]; + NSCAssert(certsData.length, @"No data read from %@", path); + NSString *certsString = [[NSString alloc] initWithData:certsData encoding:NSUTF8StringEncoding]; + return grpc_ssl_credentials_create(certsString.UTF8String, 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, ^{ // Do not use NSBundle.mainBundle, as it's nil for tests of library projects. @@ -47,15 +59,34 @@ 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); + kDefaultCertificates = CertificatesAtPath(certsPath); }); - return (self = [super initWithChannel:grpc_secure_channel_create(kCredentials, - host.UTF8String, - NULL) - hostName:host]); + grpc_credentials *certificates = path ? CertificatesAtPath(path) : kDefaultCertificates; + + // 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)]); +} + +- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel { + [NSException raise:NSInternalInconsistencyException format:@"use another initializer"]; + return [self initWithHost:nil]; // silence warnings } @end diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m index 4941cbc3dd1dc1e328e5796e4c764da2aaa7b997..5decfba7e36cdc68ad81f8e4a1f42f9c75da88c5 100644 --- a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m @@ -38,8 +38,11 @@ @implementation GRPCUnsecuredChannel - (instancetype)initWithHost:(NSString *)host { - return (self = [super initWithChannel:grpc_insecure_channel_create(host.UTF8String, NULL) - hostName:host]); + return (self = [super initWithChannel:grpc_insecure_channel_create(host.UTF8String, NULL)]); } +- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel { + [NSException raise:NSInternalInconsistencyException format:@"use the other initializer"]; + return [self initWithHost:nil]; // silence warnings +} @end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index db062336aaf4f9a327a956149fc885e1866c72c9..951c0510369fda65442fa12b39fabb783178eb52 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -38,7 +38,8 @@ #include <grpc/byte_buffer.h> #include <grpc/support/alloc.h> -#import "GRPCChannel.h" +#import "GRPCCompletionQueue.h" +#import "GRPCHost.h" #import "NSDictionary+GRPC.h" #import "NSData+GRPC.h" #import "NSError+GRPC.h" @@ -223,8 +224,8 @@ #pragma mark GRPCWrappedCall -@implementation GRPCWrappedCall{ - GRPCChannel *_channel; +@implementation GRPCWrappedCall { + GRPCCompletionQueue *_queue; grpc_call *_call; } @@ -245,8 +246,12 @@ grpc_init(); }); - _channel = [GRPCChannel channelToHost:host]; - _call = [_channel unmanagedCallWithPath:path]; + // 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]; + + _call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path completionQueue:_queue]; if (_call == NULL) { return nil; }