From bd993df3f6eeaea7b032b272703c5fc41beeebef Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Sat, 1 Aug 2015 02:51:22 -0700 Subject: Encapsulate grpc_call creation inside GRPCChannel --- src/objective-c/GRPCClient/private/GRPCChannel.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/objective-c/GRPCClient/private/GRPCChannel.h') diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h index bc6a47d469..49f5bfcc18 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -33,19 +33,19 @@ #import -struct grpc_channel; +#include -// 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. +// To create a grpc_call to that host, use |unmanagedCallWithPath|. Release this object when the +// call is finished. @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; +- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel + hostName:(NSString *)hostName NS_DESIGNATED_INITIALIZER; -- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER; +- (grpc_call *)unmanagedCallWithPath:(NSString *)path; @end -- cgit v1.2.3 From 148403af988522e61f4e71dc1bbd00b9f0d51a42 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Sat, 1 Aug 2015 02:56:04 -0700 Subject: Remove GRPCChannel-initWithHost to simplify implementation --- src/objective-c/GRPCClient/private/GRPCChannel.h | 1 - src/objective-c/GRPCClient/private/GRPCChannel.m | 22 +++++++++------------- .../GRPCClient/private/GRPCSecureChannel.h | 2 +- .../GRPCClient/private/GRPCUnsecuredChannel.h | 2 +- 4 files changed, 11 insertions(+), 16 deletions(-) (limited to 'src/objective-c/GRPCClient/private/GRPCChannel.h') diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h index 49f5bfcc18..49f8b712b9 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -43,7 +43,6 @@ // Convenience constructor to allow for reuse of connections. + (instancetype)channelToHost:(NSString *)host; -- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER; - (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel hostName:(NSString *)hostName NS_DESIGNATED_INITIALIZER; diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m index 44f64e704a..90973cd83d 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCChannel.m @@ -54,17 +54,13 @@ }); GRPCChannel *channel = channelCache[host]; if (!channel) { - channel = [[self alloc] initWithHost:host]; + channel = [self uncachedChannelToHost:host]; channelCache[host] = channel; } return channel; } -- (instancetype)init { - return [self initWithHost:nil]; -} - -- (instancetype)initWithHost:(NSString *)host { ++ (instancetype)uncachedChannelToHost:(NSString *)host { if (![host rangeOfString:@"://"].length) { // No scheme provided; assume https. host = [@"https://" stringByAppendingString:host]; @@ -86,6 +82,10 @@ return nil; // silence warning. } +- (instancetype)init { + return [self initWithChannel:NULL hostName:nil]; +} + - (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel hostName:(NSString *)hostName { if (!unmanagedChannel || !hostName) { @@ -113,12 +113,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 diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h index d34ceaea0c..8c5fe33d61 100644 --- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h @@ -34,5 +34,5 @@ #import "GRPCChannel.h" @interface GRPCSecureChannel : GRPCChannel - +- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h index 9d89cfb541..8528be44c0 100644 --- a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h @@ -34,5 +34,5 @@ #import "GRPCChannel.h" @interface GRPCUnsecuredChannel : GRPCChannel - +- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER; @end -- cgit v1.2.3 From e8543b071502fe0609d0c1657cfaf39d1a04c25d Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Sat, 1 Aug 2015 17:37:40 -0700 Subject: Let register SSL config per-host. Surfaced in GRPCCall+Tests.h Add GRPCHost to store channel config, and to create channels on demand with that config. GRPCChannels and configs are cached together. GRPCSecureChannel is now initialized with (nullable) path to a certificates file and (nullable) name override. The same mechanism will be used for creating insecure channels, removing the ability to do it by specifying the HTTP scheme in the address (which was deemed too subtle for its implications). --- src/objective-c/GRPCClient/GRPCCall+Tests.h | 45 ++++++++ src/objective-c/GRPCClient/GRPCCall+Tests.m | 47 ++++++++ src/objective-c/GRPCClient/private/GRPCChannel.h | 12 +- src/objective-c/GRPCClient/private/GRPCChannel.m | 74 +----------- .../GRPCClient/private/GRPCCompletionQueue.m | 2 - src/objective-c/GRPCClient/private/GRPCHost.h | 56 +++++++++ src/objective-c/GRPCClient/private/GRPCHost.m | 126 +++++++++++++++++++++ .../GRPCClient/private/GRPCSecureChannel.h | 12 +- .../GRPCClient/private/GRPCSecureChannel.m | 51 +++++++-- .../GRPCClient/private/GRPCUnsecuredChannel.m | 7 +- .../GRPCClient/private/GRPCWrappedCall.m | 15 ++- 11 files changed, 350 insertions(+), 97 deletions(-) create mode 100644 src/objective-c/GRPCClient/GRPCCall+Tests.h create mode 100644 src/objective-c/GRPCClient/GRPCCall+Tests.m create mode 100644 src/objective-c/GRPCClient/private/GRPCHost.h create mode 100644 src/objective-c/GRPCClient/private/GRPCHost.m (limited to 'src/objective-c/GRPCClient/private/GRPCChannel.h') diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h new file mode 100644 index 0000000000..3d617b05d9 --- /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 0000000000..7c5b81d661 --- /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 49f8b712b9..fa9f6f28d1 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -36,15 +36,9 @@ #include // 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 90973cd83d..68a402db97 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 - -#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 12535c9616..696069c200 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 0000000000..86e43a283d --- /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 + +#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 0000000000..405545b86b --- /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 8c5fe33d61..8b259c8dad 100644 --- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h @@ -31,8 +31,18 @@ * */ +#import + #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 2dbbd52087..c783462dd3 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_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 4941cbc3dd..5decfba7e3 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 db062336aa..951c051036 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -38,7 +38,8 @@ #include #include -#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; } -- cgit v1.2.3 From 56c6574652ce261370582fec7decd4a8e6ce1cb3 Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Mon, 3 Aug 2015 16:23:20 -0700 Subject: Fixup: GRPCChannel.unmanagedChannel back to readonly --- src/objective-c/GRPCClient/private/GRPCChannel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/objective-c/GRPCClient/private/GRPCChannel.h') diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h index fa9f6f28d1..de0b58ffe4 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -38,7 +38,7 @@ // 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) grpc_channel *unmanagedChannel; +@property(nonatomic, readonly) grpc_channel *unmanagedChannel; - (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER; @end -- cgit v1.2.3 From 0607bae87e0f3417972bc221a5c4f8dc6bec5fef Mon Sep 17 00:00:00 2001 From: Jorge Canizales Date: Wed, 5 Aug 2015 16:36:10 -0700 Subject: Forward-declare grpc_channel and specify ownership semantics --- src/objective-c/GRPCClient/private/GRPCChannel.h | 8 +++++--- src/objective-c/GRPCClient/private/GRPCChannel.m | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/objective-c/GRPCClient/private/GRPCChannel.h') diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h index de0b58ffe4..2a7b701576 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -33,12 +33,14 @@ #import -#include +struct grpc_channel; // 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) grpc_channel *unmanagedChannel; +@property(nonatomic, readonly) struct grpc_channel *unmanagedChannel; -- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel 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 diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m index 49d6008fd4..4366e63320 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCChannel.m @@ -33,6 +33,8 @@ #import "GRPCChannel.h" +#include + @implementation GRPCChannel - (instancetype)init { -- cgit v1.2.3