aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Kristopher Wuollett <klw@google.com>2016-01-31 14:26:39 -0500
committerGravatar Kristopher Wuollett <klw@google.com>2016-01-31 14:26:39 -0500
commitc6b301775c4a783dec72f3939e489bce8eadf8a5 (patch)
treebb432f52a53cc9dae14e0af24209f2fbeaeb7a00
parent01ba279fdddb28096893a4e09a62b915cdbca6be (diff)
Add channel args to enable user agent string for Objective-C
-rw-r--r--examples/objective-c/helloworld/main.m2
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+ChannelArg.h52
-rw-r--r--src/objective-c/GRPCClient/GRPCCall+ChannelArg.m58
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.h2
-rw-r--r--src/objective-c/GRPCClient/private/GRPCHost.m18
-rw-r--r--src/objective-c/GRPCClient/private/GRPCSecureChannel.h15
-rw-r--r--src/objective-c/GRPCClient/private/GRPCSecureChannel.m27
-rw-r--r--src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.h6
-rw-r--r--src/objective-c/GRPCClient/private/GRPCUnsecuredChannel.m13
-rw-r--r--src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.h29
-rw-r--r--src/objective-c/GRPCClient/private/GRPCWrappedChannelArgs.m188
11 files changed, 385 insertions, 25 deletions
diff --git a/examples/objective-c/helloworld/main.m b/examples/objective-c/helloworld/main.m
index a62f8362a2..f390b5a0f9 100644
--- a/examples/objective-c/helloworld/main.m
+++ b/examples/objective-c/helloworld/main.m
@@ -34,6 +34,7 @@
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
+#import <GRPCClient/GRPCCall+ChannelArg.h>
#import <GRPCClient/GRPCCall+Tests.h>
#import <HelloWorld/Helloworld.pbrpc.h>
@@ -42,6 +43,7 @@ static NSString * const kHostAddress = @"localhost:50051";
int main(int argc, char * argv[]) {
@autoreleasepool {
[GRPCCall useInsecureConnectionsForHost:kHostAddress];
+ [GRPCCall usePrimaryUserAgent:@"HelloWorld/1.0" forHost:kHostAddress];
HLWGreeter *client = [[HLWGreeter alloc] initWithHost:kHostAddress];
diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
new file mode 100644
index 0000000000..11b2babbe7
--- /dev/null
+++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2016, 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"
+
+/**
+ * Methods to configure GRPC channel options for specific hosts.
+ */
+@interface GRPCCall (ChannelArg)
+
+/**
+ * Use the provided @c primaryUserAgent at the beginning of the HTTP User Agent string for the
+ * provided @c host.
+ */
++ (void)usePrimaryUserAgent:(NSString *)primaryUserAgent forHost:(NSString *)host;
+
+/**
+ * Use the provided @c secondaryUserAgent at the end of the HTTP User Agent string for the
+ * provided @c host.
+ */
++ (void)useSecondaryUserAgent:(NSString *)secondaryUserAgent forHost:(NSString *)host;
+
+@end
diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
new file mode 100644
index 0000000000..159e12c7e8
--- /dev/null
+++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.m
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright 2016, 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+ChannelArg.h"
+
+#import "private/GRPCHost.h"
+
+@implementation GRPCCall (ChannelArg)
+
++ (void)usePrimaryUserAgent:(NSString *)primaryUserAgent forHost:(NSString *)host {
+ if (!primaryUserAgent || !host) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"primaryUserAgent and host must be provided."];
+ }
+ GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
+ hostConfig.primaryUserAgent = primaryUserAgent;
+}
+
++ (void)useSecondaryUserAgent:(NSString *)secondaryUserAgent forHost:(NSString *)host {
+ if (!secondaryUserAgent || !host) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"secondaryUserAgent and host must be provided."];
+ }
+ GRPCHost *hostConfig = [GRPCHost hostWithAddress:host];
+ hostConfig.secondaryUserAgent = secondaryUserAgent;
+}
+
+@end
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 <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.
@@ -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 <grpc/grpc.h>
+#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 <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 \ 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 <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