From c6b301775c4a783dec72f3939e489bce8eadf8a5 Mon Sep 17 00:00:00 2001 From: Kristopher Wuollett Date: Sun, 31 Jan 2016 14:26:39 -0500 Subject: Add channel args to enable user agent string for Objective-C --- src/objective-c/GRPCClient/private/GRPCHost.h | 2 + src/objective-c/GRPCClient/private/GRPCHost.m | 18 +- .../GRPCClient/private/GRPCSecureChannel.h | 15 +- .../GRPCClient/private/GRPCSecureChannel.m | 27 ++- .../GRPCClient/private/GRPCUnsecuredChannel.h | 6 +- .../GRPCClient/private/GRPCUnsecuredChannel.m | 13 +- .../GRPCClient/private/GRPCWrappedChannelArgs.h | 29 ++++ .../GRPCClient/private/GRPCWrappedChannelArgs.m | 188 +++++++++++++++++++++ 8 files changed, 273 insertions(+), 25 deletions(-) create mode 100644 src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.h create mode 100644 src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.m (limited to 'src/objective-c/GRPCClient/private') diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h index 6b4f98746d..673ed9060e 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.h +++ b/src/objective-c/GRPCClient/private/GRPCHost.h @@ -39,6 +39,8 @@ struct grpc_call; @interface GRPCHost : NSObject @property(nonatomic, readonly) NSString *address; +@property(nonatomic, copy) NSString *primaryUserAgent; +@property(nonatomic, copy) NSString *secondaryUserAgent; /** The following properties should only be modified for testing: */ diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m index a8cd3a0e74..3ea89104d0 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.m +++ b/src/objective-c/GRPCClient/private/GRPCHost.m @@ -39,6 +39,7 @@ #import "GRPCCompletionQueue.h" #import "GRPCSecureChannel.h" #import "GRPCUnsecuredChannel.h" +#import "GRPCWrappedChannelArgs.h" @interface GRPCHost () // TODO(mlumish): Investigate whether caching channels with strong links is a good idea. @@ -107,12 +108,25 @@ // 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]; + if (_primaryUserAgent) { + [argsBuilder addKey:@GRPC_ARG_PRIMARY_USER_AGENT_STRING stringValue:_primaryUserAgent]; + } + if (_secondaryUserAgent) { + [argsBuilder addKey:@GRPC_ARG_SECONDARY_USER_AGENT_STRING stringValue:_secondaryUserAgent]; + } + if (_secure) { + if (_hostNameOverride) { + [argsBuilder addKey:@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG stringValue:_hostNameOverride]; + } + _channel = [[GRPCSecureChannel alloc] initWithHost:_address pathToCertificates:_pathToCertificates - hostNameOverride:_hostNameOverride]; + channelArgs:[argsBuilder build]]; } else { - _channel = [[GRPCUnsecuredChannel alloc] initWithHost:_address]; + _channel = [[GRPCUnsecuredChannel alloc] initWithHost:_address + channelArgs:[argsBuilder build]]; } } return _channel; diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h index b82b9fe35a..bbe01081cb 100644 --- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.h @@ -35,21 +35,26 @@ #import "GRPCChannel.h" +@class GRPCWrappedChannelArgs; + struct grpc_channel_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. + * 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 - hostNameOverride:(NSString *)hostNameOverride; + channelArgs:(GRPCWrappedChannelArgs *)channelArgs; -/** The passed arguments aren't required to be valid beyond the invocation of this initializer. */ +/** + * 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 - args:(grpc_channel_args *)args NS_DESIGNATED_INITIALIZER; + channelArgs:(GRPCWrappedChannelArgs *)channelArgs NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m index a573c171e9..156ff9b344 100644 --- a/src/objective-c/GRPCClient/private/GRPCSecureChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCSecureChannel.m @@ -34,6 +34,7 @@ #import "GRPCSecureChannel.h" #include +#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. @@ -55,12 +56,12 @@ static grpc_channel_credentials *CertificatesAtPath(NSString *path, NSError **er @implementation GRPCSecureChannel - (instancetype)initWithHost:(NSString *)host { - return [self initWithHost:host pathToCertificates:nil hostNameOverride:nil]; + return [self initWithHost:host pathToCertificates:nil channelArgs:nil]; } - (instancetype)initWithHost:(NSString *)host pathToCertificates:(NSString *)path - hostNameOverride:(NSString *)hostNameOverride { + channelArgs:(GRPCWrappedChannelArgs *)channelArgs { // Load default SSL certificates once. static grpc_channel_credentials *kDefaultCertificates; static dispatch_once_t loading; @@ -86,26 +87,20 @@ static grpc_channel_credentials *CertificatesAtPath(NSString *path, NSError **er 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]; + return [self initWithHost:host credentials:certificates channelArgs:channelArgs]; } - (instancetype)initWithHost:(NSString *)host credentials:(grpc_channel_credentials *)credentials - args:(grpc_channel_args *)args { + 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)]); + credentials, host.UTF8String, &args, NULL)]); } // TODO(jcanizales): GRPCSecureChannel and GRPCUnsecuredChannel are just convenience initializers diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h index 8528be44c0..b3190e8b0f 100644 --- a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h @@ -33,6 +33,10 @@ #import "GRPCChannel.h" +@class GRPCWrappedChannelArgs; + @interface GRPCUnsecuredChannel : GRPCChannel -- (instancetype)initWithHost:(NSString *)host NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithHost:(NSString *)host; +- (instancetype)initWithHost:(NSString *)host + channelArgs:(GRPCWrappedChannelArgs *)channelArgs NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m index 15b6ffc75c..f349bb7783 100644 --- a/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m @@ -34,11 +34,22 @@ #import "GRPCUnsecuredChannel.h" #include +#import "GRPCWrappedChannelArgs.h" @implementation GRPCUnsecuredChannel - (instancetype)initWithHost:(NSString *)host { - return (self = [super initWithChannel:grpc_insecure_channel_create(host.UTF8String, NULL, NULL)]); + 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 diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.h b/src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.h new file mode 100644 index 0000000000..3f1f5e6dee --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.h @@ -0,0 +1,29 @@ +#import + +#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 \ No newline at end of file diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.m b/src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.m new file mode 100644 index 0000000000..c91b670db7 --- /dev/null +++ b/src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.m @@ -0,0 +1,188 @@ +#import "GRPCWrappedChannelArgs.h" + +#import + +#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 -- cgit v1.2.3