mirror of https://github.com/grpc/grpc.git
parent
85bf574106
commit
2c6d2bd327
9 changed files with 193 additions and 519 deletions
@ -1,60 +0,0 @@ |
||||
/*
|
||||
* |
||||
* 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 |
@ -1,113 +0,0 @@ |
||||
/* |
||||
* |
||||
* 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 |
@ -1,42 +0,0 @@ |
||||
/*
|
||||
* |
||||
* 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 |
@ -1,60 +0,0 @@ |
||||
/* |
||||
* |
||||
* 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 |
@ -1,29 +0,0 @@ |
||||
#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 |
@ -1,188 +0,0 @@ |
||||
#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 |
Loading…
Reference in new issue