aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/objective-c/GRPCClient
diff options
context:
space:
mode:
authorGravatar Julien Boeuf <jboeuf@google.com>2015-08-06 15:29:45 -0700
committerGravatar Julien Boeuf <jboeuf@google.com>2015-08-06 15:29:45 -0700
commitf3128e6ef3d5aab97216a23e052bba85a6829bfd (patch)
tree7c42ebf0f3c1a936cc3a751002324d4cb13a7519 /src/objective-c/GRPCClient
parent54b5018dd889cbc5059ff77ba083ff7020c63a9b (diff)
parent83f8416f4ccaf1fa50d45f80124c72342244b7bd (diff)
Merge branch 'master' of https://github.com/grpc/grpc into server_creds_auth_md_processor
Diffstat (limited to 'src/objective-c/GRPCClient')
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+Tests.h45
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+Tests.m47
-rw-r--r--src/objective-c/GRPCClient/GRPCCall.m12
-rw-r--r--src/objective-c/GRPCClient/private/GRPCChannel.h13
-rw-r--r--src/objective-c/GRPCClient/private/GRPCChannel.m56
-rw-r--r--src/objective-c/GRPCClient/private/GRPCCompletionQueue.m2
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.h58
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.m134
-rw-r--r--src/objective-c/GRPCClient/private/GRPCSecureChannel.h15
-rw-r--r--src/objective-c/GRPCClient/private/GRPCSecureChannel.m78
-rw-r--r--src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h2
-rw-r--r--src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m6
-rw-r--r--src/objective-c/GRPCClient/private/GRPCWrappedCall.h7
-rw-r--r--src/objective-c/GRPCClient/private/GRPCWrappedCall.m39
14 files changed, 411 insertions, 103 deletions
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/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m
index 9d9648ae28..5f7d74bca8 100644
--- a/src/objective-c/GRPCClient/GRPCCall.m
+++ b/src/objective-c/GRPCClient/GRPCCall.m
@@ -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);
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h
index bc6a47d469..2a7b701576 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.h
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.h
@@ -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
diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m
index af4326332f..4366e63320 100644
--- a/src/objective-c/GRPCClient/private/GRPCChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCChannel.m
@@ -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
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..f0bbd53023
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCHost.h
@@ -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
diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m
new file mode 100644
index 0000000000..5d9c48a524
--- /dev/null
+++ b/src/objective-c/GRPCClient/private/GRPCHost.m
@@ -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
diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
index d34ceaea0c..74257eb058 100644
--- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
+++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h
@@ -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
diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m
index 43a8bd539e..9b4b6768f8 100644
--- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m
@@ -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
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
diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m
index 8518f78c5b..070a529629 100644
--- a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m
+++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m
@@ -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
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
index 18f8bb5531..da11cbb761 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h
@@ -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;
diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
index 1db63df77f..951c051036 100644
--- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
+++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m
@@ -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,38 +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,
- _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;
}
@@ -299,4 +300,4 @@
grpc_call_destroy(_call);
}
-@end \ No newline at end of file
+@end