Skip to content
Snippets Groups Projects
Commit 2c6d2bd3 authored by Kristopher Wuollett's avatar Kristopher Wuollett
Browse files

Removed (un)secure channel subclasses

parent 85bf5741
No related branches found
No related tags found
No related merge requests found
......@@ -33,18 +33,51 @@
#import <Foundation/Foundation.h>
struct grpc_channel;
#include <grpc/grpc.h>
struct grpc_channel_credentials;
/**
* 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;
@property(nonatomic, readonly, nonnull) struct grpc_channel *unmanagedChannel;
@property(nonatomic, readonly, getter=isSecure) BOOL secure;
- (nullable instancetype)init NS_UNAVAILABLE;
/**
* Creates a secure channel to the specified @c host using default credentials and channel
* arguments. If certificates could not be found to create a secure channel, then @c nil is
* returned.
*/
+ (nullable GRPCChannel *)secureChannelWithHost:(nonnull NSString *)host;
/**
* Creates a secure channel to the specified @c host using the specified @c pathToCertificates and
* @c channelArgs. Only in tests should @c pathToCertificates be nil or
* @c GRPC_SSL_TARGET_NAME_OVERRIDE_ARG channel arg be set. Passing nil for @c pathToCertificates
* results in using the default root certificates distributed with the library. If certificates
* could not be found in any case, then @c nil is returned.
*/
+ (nullable GRPCChannel *)secureChannelWithHost:(nonnull NSString *)host
pathToCertificates:(nullable NSString *)pathToCertificates
channelArgs:(nullable NSDictionary *)channelArgs;
/**
* 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.
* Creates a secure channel to the specified @c host using the specified @c credentials and
* @c channelArgs. Only in tests should @c GRPC_SSL_TARGET_NAME_OVERRIDE_ARG channel arg be set.
*/
- (instancetype)initWithChannel:(struct grpc_channel *)unmanagedChannel NS_DESIGNATED_INITIALIZER;
+ (nonnull GRPCChannel *)secureChannelWithHost:(nonnull NSString *)host
credentials:(nonnull struct grpc_channel_credentials *)credentials
channelArgs:(nullable NSDictionary *)channelArgs;
/**
* Creates an insecure channel to the specified @c host using the specified @c channelArgs.
*/
+ (nonnull GRPCChannel *)insecureChannelWithHost:(nonnull NSString *)host
channelArgs:(nullable NSDictionary *)channelArgs;
@end
......@@ -33,22 +33,101 @@
#import "GRPCChannel.h"
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
@implementation GRPCChannel
/**
* Returns @c grpc_channel_credentials from the specifie @c path. If the file at the path could not
* be read then NULL is returned. If NULL is returned, @c errorPtr may not be NULL if there are
* details available describing what went wrong.
*/
static grpc_channel_credentials *CertificatesAtPath(NSString *path, NSError **errorPtr) {
// Files in PEM format can have non-ASCII characters in their comments (e.g. for the name of the
// issuer). Load them as UTF8 and produce an ASCII equivalent.
NSString *contentInUTF8 = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:errorPtr];
NSData *contentInASCII = [contentInUTF8 dataUsingEncoding:NSASCIIStringEncoding
allowLossyConversion:YES];
if (!contentInASCII.bytes) {
// Passing NULL to grpc_ssl_credentials_create produces behavior we don't want, so return.
return NULL;
}
return grpc_ssl_credentials_create(contentInASCII.bytes, NULL, NULL);
}
/**
* Allocates a @c grpc_channel_args and populates it with the options specified in the
* @c dictionary. Keys must be @c NSString. If the value responds to @c @selector(UTF8String) then
* it will be mapped to @c GRPC_ARG_STRING. If not, it will be mapped to @c GRPC_ARG_INTEGER if the
* value responds to @c @selector(intValue). Otherwise, an exception will be raised.
*/
grpc_channel_args * buildChannelArgs(NSDictionary *dictionary) {
if (!dictionary) {
return NULL;
}
NSUInteger argCount = [dictionary count];
// Allocate memory for both the individual args and their container
grpc_channel_args *channelArgs = gpr_malloc(sizeof(grpc_channel_args)
+ argCount * sizeof(grpc_arg));
channelArgs->num_args = argCount;
channelArgs->args = (grpc_arg *) (channelArgs + sizeof(grpc_channel_args));
- (instancetype)init {
return [self initWithChannel:NULL];
__block NSUInteger argIndex = 0;
[dictionary enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj,
BOOL * _Nonnull stop) {
// use of UTF8String assumes that grpc won't modify the pointers
grpc_arg *arg = &channelArgs->args[argIndex++];
arg->key = (char *) [key UTF8String]; // allow exception to be raised if not supported
if ([obj respondsToSelector:@selector(UTF8String)]) {
arg->type = GRPC_ARG_STRING;
arg->value.string = (char *) [obj UTF8String];
} else if ([obj respondsToSelector:@selector(intValue)]) {
arg->type = GRPC_ARG_INTEGER;
arg->value.integer = [obj intValue];
} else {
[NSException raise:NSInvalidArgumentException format:@"Invalid value type: %@", [obj class]];
}
}];
return channelArgs;
}
@implementation GRPCChannel {
// Retain arguments to channel_create because they may not be used on the thread that invoked
// the channel_create function.
NSString *_host;
grpc_channel_args *_channelArgs;
}
// Designated initializer
- (instancetype)initWithChannel:(grpc_channel *)unmanagedChannel {
if (!unmanagedChannel) {
- (instancetype)initWithHost:(NSString *)host
secure:(BOOL)secure
credentials:(struct grpc_channel_credentials *)credentials
channelArgs:(NSDictionary *)channelArgs {
if (!host) {
[NSException raise:NSInvalidArgumentException format:@"host argument missing"];
}
if (secure && !credentials) {
return nil;
}
if ((self = [super init])) {
_unmanagedChannel = unmanagedChannel;
if (self = [super init]) {
_channelArgs = buildChannelArgs(channelArgs);
_host = [host copy];
if (secure) {
_unmanagedChannel = grpc_secure_channel_create(credentials, _host.UTF8String, _channelArgs,
NULL);
} else {
_unmanagedChannel = grpc_insecure_channel_create(host.UTF8String, _channelArgs, NULL);
}
}
return self;
}
......@@ -56,5 +135,61 @@
// 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);
gpr_free(_channelArgs);
}
+ (GRPCChannel *)secureChannelWithHost:(NSString *)host {
return [[GRPCChannel alloc] initWithHost:host secure:YES credentials:NULL channelArgs:NULL];
}
+ (GRPCChannel *)secureChannelWithHost:(NSString *)host
pathToCertificates:(NSString *)path
channelArgs:(NSDictionary *)channelArgs {
// Load default SSL certificates once.
static grpc_channel_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 *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);
});
//TODO(jcanizales): Add NSError** parameter to the initializer.
grpc_channel_credentials *certificates = path
? CertificatesAtPath(path, NULL)
: kDefaultCertificates;
return [[GRPCChannel alloc] initWithHost:host
secure:YES
credentials:certificates
channelArgs:channelArgs];
}
+ (GRPCChannel *)secureChannelWithHost:(NSString *)host
credentials:(struct grpc_channel_credentials *)credentials
channelArgs:(NSDictionary *)channelArgs {
return [[GRPCChannel alloc] initWithHost:host
secure:YES
credentials:credentials
channelArgs:channelArgs];
}
+ (GRPCChannel *)insecureChannelWithHost:(NSString *)host
channelArgs:(NSDictionary *)channelArgs {
return [[GRPCChannel alloc] initWithHost:host
secure:NO
credentials:NULL
channelArgs:channelArgs];
}
@end
......@@ -37,9 +37,7 @@
#import "GRPCChannel.h"
#import "GRPCCompletionQueue.h"
#import "GRPCSecureChannel.h"
#import "GRPCUnsecuredChannel.h"
#import "GRPCWrappedChannelArgs.h"
#import "NSDictionary+GRPC.h"
@interface GRPCHost ()
// TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
......@@ -107,26 +105,26 @@
- (GRPCChannel *)channel {
// Create it lazily, because we don't want to open a connection just because someone is
// configuring a host.
if (!_channel) {
GRPCWrappedChannelArgsBuilder *argsBuilder = [[GRPCWrappedChannelArgsBuilder alloc] init];
NSMutableDictionary *args = [NSMutableDictionary dictionary];
if (_primaryUserAgent) {
[argsBuilder addKey:@GRPC_ARG_PRIMARY_USER_AGENT_STRING stringValue:_primaryUserAgent];
args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = _primaryUserAgent;
}
if (_secondaryUserAgent) {
[argsBuilder addKey:@GRPC_ARG_SECONDARY_USER_AGENT_STRING stringValue:_secondaryUserAgent];
args[@GRPC_ARG_SECONDARY_USER_AGENT_STRING] = _secondaryUserAgent;
}
if (_secure) {
if (_hostNameOverride) {
[argsBuilder addKey:@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG stringValue:_hostNameOverride];
args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = _hostNameOverride;
}
_channel = [[GRPCSecureChannel alloc] initWithHost:_address
pathToCertificates:_pathToCertificates
channelArgs:[argsBuilder build]];
_channel = [GRPCChannel secureChannelWithHost:_address
pathToCertificates:_pathToCertificates
channelArgs:args];
} else {
_channel = [[GRPCUnsecuredChannel alloc] initWithHost:_address
channelArgs:[argsBuilder build]];
_channel = [GRPCChannel insecureChannelWithHost:_address channelArgs:args];
}
}
return _channel;
......
/*
*
* 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.
*
*/
#include <grpc/grpc.h>
#import "GRPCChannel.h"
@class GRPCWrappedChannelArgs;
struct grpc_channel_credentials;
@interface GRPCSecureChannel : GRPCChannel
- (instancetype)initWithHost:(NSString *)host;
/**
* Only in tests should pathToCertificates or @c GRPC_SSL_TARGET_NAME_OVERRIDE_ARG arg be set.
* Passing nil for pathToCertificates results in using the default root certificates distributed
* with the library.
*/
- (instancetype)initWithHost:(NSString *)host
pathToCertificates:(NSString *)path
channelArgs:(GRPCWrappedChannelArgs *)channelArgs;
/**
* The passed arguments are copied and no longer needed after the invocation of this initializer.
*/
- (instancetype)initWithHost:(NSString *)host
credentials:(struct grpc_channel_credentials *)credentials
channelArgs:(GRPCWrappedChannelArgs *)channelArgs NS_DESIGNATED_INITIALIZER;
@end
/*
*
* 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 "GRPCSecureChannel.h"
#include <grpc/grpc_security.h>
#import "GRPCWrappedChannelArgs.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_channel_credentials *CertificatesAtPath(NSString *path, NSError **errorPtr) {
// Files in PEM format can have non-ASCII characters in their comments (e.g. for the name of the
// issuer). Load them as UTF8 and produce an ASCII equivalent.
NSString *contentInUTF8 = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:errorPtr];
NSData *contentInASCII = [contentInUTF8 dataUsingEncoding:NSASCIIStringEncoding
allowLossyConversion:YES];
if (!contentInASCII.bytes) {
// Passing NULL to grpc_ssl_credentials_create produces behavior we don't want, so return.
return NULL;
}
return grpc_ssl_credentials_create(contentInASCII.bytes, NULL, NULL);
}
@implementation GRPCSecureChannel
- (instancetype)initWithHost:(NSString *)host {
return [self initWithHost:host pathToCertificates:nil channelArgs:nil];
}
- (instancetype)initWithHost:(NSString *)host
pathToCertificates:(NSString *)path
channelArgs:(GRPCWrappedChannelArgs *)channelArgs {
// Load default SSL certificates once.
static grpc_channel_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 *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);
});
//TODO(jcanizales): Add NSError** parameter to the initializer.
grpc_channel_credentials *certificates = path
? CertificatesAtPath(path, NULL)
: kDefaultCertificates;
if (!certificates) {
return nil;
}
return [self initWithHost:host credentials:certificates channelArgs:channelArgs];
}
- (instancetype)initWithHost:(NSString *)host
credentials:(grpc_channel_credentials *)credentials
channelArgs:(GRPCWrappedChannelArgs *)channelArgs {
grpc_channel_args args = (grpc_channel_args) { .num_args = 0, .args = NULL };
if (channelArgs) {
args = channelArgs.channelArgs;
}
return (self = [super
initWithChannel:grpc_secure_channel_create(
credentials, host.UTF8String, &args, 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 another initializer"];
return [self initWithHost:nil]; // silence warnings
}
@end
/*
*
* 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 "GRPCChannel.h"
@class GRPCWrappedChannelArgs;
@interface GRPCUnsecuredChannel : GRPCChannel
- (instancetype)initWithHost:(NSString *)host;
- (instancetype)initWithHost:(NSString *)host
channelArgs:(GRPCWrappedChannelArgs *)channelArgs NS_DESIGNATED_INITIALIZER;
@end
/*
*
* 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 "GRPCUnsecuredChannel.h"
#include <grpc/grpc.h>
#import "GRPCWrappedChannelArgs.h"
@implementation GRPCUnsecuredChannel
- (instancetype)initWithHost:(NSString *)host {
return [self initWithHost:host channelArgs:nil];
}
- (instancetype)initWithHost:(NSString *)host channelArgs:(GRPCWrappedChannelArgs *)channelArgs {
grpc_channel_args args = (grpc_channel_args) { .num_args = 0, .args = NULL };
if (channelArgs) {
args = channelArgs.channelArgs;
}
return (self = [super
initWithChannel:grpc_insecure_channel_create(host.UTF8String, &args, 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
#import <grpc/grpc.h>
#pragma mark - Wrapped Channel Arguments
/**
* A wrapper @c grpc_channel_args that frees allocated memory used to copy key / value pairs by the
* @c GRPCWrappedChannelArgsBuilder.
*/
@interface GRPCWrappedChannelArgs : NSObject
@property(nonatomic, readonly) grpc_channel_args channelArgs;
- (instancetype)init NS_UNAVAILABLE;
@end
#pragma mark - Wrapped Channel Arguments Builder
/**
* A builder that simplifies construction and usage of @c grpc_channel_args by building a
* @c GRPCWrappedChannelArgs.
*/
@interface GRPCWrappedChannelArgsBuilder : NSObject
- (instancetype)addKey:(NSString *)key stringValue:(NSString *)value;
- (instancetype)addKey:(NSString *)key integerValue:(int)value;
- (GRPCWrappedChannelArgs *)build;
@end
#import "GRPCWrappedChannelArgs.h"
#import <grpc/support/log.h>
#pragma mark - Argument Types
@interface GRPCWrappedChannelArg : NSObject
@property(nonatomic, readonly) NSString *grpc_key;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithKey:(NSString *)key NS_DESIGNATED_INITIALIZER;
@end
@implementation GRPCWrappedChannelArg {
NSString *grpc_key_;
}
- (instancetype)initWithKey:(NSString *)key {
GPR_ASSERT(key);
if (self = [super init]) {
grpc_key_ = [key copy];
}
return self;
}
- (NSString *)grpc_key {
return grpc_key_;
}
@end
#pragma mark String Argument Type
@interface GRPCWrappedChannelStringArg : GRPCWrappedChannelArg
@property(nonatomic, readonly) NSString *grpc_string;
- (instancetype)initWithKey:(NSString *)key NS_UNAVAILABLE;
- (instancetype)initWithKey:(NSString *)key value:(NSString *)value NS_DESIGNATED_INITIALIZER;
@end
@implementation GRPCWrappedChannelStringArg {
NSString *grpc_value_;
}
- (instancetype)initWithKey:(NSString *)key value:(NSString *)value {
GPR_ASSERT(value);
if (self = [super initWithKey:key]) {
grpc_value_ = [value copy];
}
return self;
}
- (NSString *)grpc_string {
return grpc_value_;
}
@end
#pragma mark Integer Argument Type
@interface GRPCWrappedChannelIntegerArg : GRPCWrappedChannelArg
@property(nonatomic, readonly) int grpc_integer;
- (instancetype)initWithKey:(NSString *)key NS_UNAVAILABLE;
- (instancetype)initWithKey:(NSString *)key value:(int)value NS_DESIGNATED_INITIALIZER;
@end
@implementation GRPCWrappedChannelIntegerArg {
int grpc_value_;
}
- (instancetype)initWithKey:(NSString *)key value:(int)value {
if (self = [super initWithKey:key]) {
grpc_value_ = value;
}
return self;
}
- (int)grpc_integer {
return grpc_value_;
}
@end
#pragma mark - Wrapped Channel Arguments
@interface GRPCWrappedChannelArgs ()
- (instancetype)initWithChannelArgs:(grpc_channel_args)channelArgs;
@end
@implementation GRPCWrappedChannelArgs {
grpc_channel_args channelArgs_;
}
- (instancetype)initWithChannelArgs:(grpc_channel_args)channelArgs {
if (self = [super init]) {
channelArgs_ = channelArgs;
}
return self;
}
- (grpc_channel_args)channelArgs {
return channelArgs_;
}
- (void)dealloc {
for (size_t i = 0; i < channelArgs_.num_args; ++i) {
grpc_arg *arg = &channelArgs_.args[i];
free(arg->key);
if (arg->type == GRPC_ARG_STRING) {
free(arg->value.string);
}
}
free(channelArgs_.args);
}
@end
#pragma mark - Wrapped Channel Arguments Builder
@implementation GRPCWrappedChannelArgsBuilder {
NSMutableArray *args_;
}
- (instancetype)init {
if (self = [super init]) {
args_ = [NSMutableArray array];
}
return self;
}
- (instancetype)addKey:(NSString *)key stringValue:(NSString *)value {
GRPCWrappedChannelStringArg *arg = [[GRPCWrappedChannelStringArg alloc] initWithKey:key value:value];
[args_ addObject:arg];
return self;
}
- (instancetype)addKey:(NSString *)key integerValue:(int)value {
GRPCWrappedChannelIntegerArg *arg = [[GRPCWrappedChannelIntegerArg alloc] initWithKey:key value:value];
[args_ addObject:arg];
return self;
}
- (GRPCWrappedChannelArgs *)build {
grpc_channel_args channelArgs;
// channelArgs.args and contents is freed by GRPCWrappedChannelArgs::dealloc
channelArgs.num_args = args_.count;
channelArgs.args = (grpc_arg *) calloc(args_.count, sizeof(grpc_arg));
for (NSInteger i = 0; i < args_.count; ++i) {
if ([args_[i] respondsToSelector:@selector(grpc_string)]) {
GRPCWrappedChannelStringArg *arg = (GRPCWrappedChannelStringArg *)args_[i];
grpc_arg *wrappedArg = &channelArgs.args[i];
wrappedArg->key = strdup(arg.grpc_key.UTF8String);
wrappedArg->type = GRPC_ARG_STRING;
wrappedArg->value.string = strdup(arg.grpc_string.UTF8String);
GPR_ASSERT(wrappedArg->key);
GPR_ASSERT(wrappedArg->value.string);
} else if ([args_[i] respondsToSelector:@selector(grpc_integer)]) {
GRPCWrappedChannelIntegerArg *arg = (GRPCWrappedChannelIntegerArg *)args_[i];
grpc_arg *wrappedArg = &channelArgs.args[i];
wrappedArg->key = strdup(arg.grpc_key.UTF8String);
wrappedArg->type = GRPC_ARG_INTEGER;
wrappedArg->value.integer = arg.grpc_integer;
GPR_ASSERT(wrappedArg->key);
} else {
GPR_ASSERT(0); // Argument type not recognized
}
}
return [[GRPCWrappedChannelArgs alloc] initWithChannelArgs:channelArgs];
}
@end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment