|
|
|
@ -28,141 +28,222 @@ |
|
|
|
|
#import "GRPCInsecureChannelFactory.h" |
|
|
|
|
#import "GRPCSecureChannelFactory.h" |
|
|
|
|
#import "version.h" |
|
|
|
|
#import "../internal/GRPCCallOptions+Internal.h" |
|
|
|
|
|
|
|
|
|
#import <GRPCClient/GRPCCall+Cronet.h> |
|
|
|
|
#import <GRPCClient/GRPCCallOptions.h> |
|
|
|
|
|
|
|
|
|
/** When all calls of a channel are destroyed, destroy the channel after this much seconds. */ |
|
|
|
|
NSTimeInterval kChannelDestroyDelay = 30; |
|
|
|
|
NSTimeInterval kDefaultChannelDestroyDelay = 30; |
|
|
|
|
|
|
|
|
|
/** Global instance of channel pool. */ |
|
|
|
|
static GRPCChannelPool *gChannelPool; |
|
|
|
|
@implementation GRPCChannelConfiguration |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Time the channel destroy when the channel's calls are unreffed. If there's new call, reset the |
|
|
|
|
* timer. |
|
|
|
|
*/ |
|
|
|
|
@interface GRPCChannelRef : NSObject |
|
|
|
|
- (nullable instancetype)initWithHost:(NSString *)host callOptions:(GRPCCallOptions *)callOptions { |
|
|
|
|
NSAssert(host.length, @"Host must not be empty."); |
|
|
|
|
NSAssert(callOptions, @"callOptions must not be empty."); |
|
|
|
|
if ((self = [super init])) { |
|
|
|
|
_host = [host copy]; |
|
|
|
|
_callOptions = [callOptions copy]; |
|
|
|
|
} |
|
|
|
|
return self; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (instancetype)initWithDestroyDelay:(NSTimeInterval)destroyDelay |
|
|
|
|
destroyChannelCallback:(void (^)())destroyChannelCallback; |
|
|
|
|
- (id<GRPCChannelFactory>)channelFactory { |
|
|
|
|
NSError *error; |
|
|
|
|
id<GRPCChannelFactory> factory; |
|
|
|
|
GRPCTransportType type = _callOptions.transportType; |
|
|
|
|
switch (type) { |
|
|
|
|
case GRPCTransportTypeChttp2BoringSSL: |
|
|
|
|
// TODO (mxyan): Remove when the API is deprecated |
|
|
|
|
#ifdef GRPC_COMPILE_WITH_CRONET |
|
|
|
|
if (![GRPCCall isUsingCronet]) { |
|
|
|
|
#endif |
|
|
|
|
factory = [GRPCSecureChannelFactory |
|
|
|
|
factoryWithPEMRootCertificates:_callOptions.PEMRootCertificates |
|
|
|
|
privateKey:_callOptions.PEMPrivateKey |
|
|
|
|
certChain:_callOptions.PEMCertChain |
|
|
|
|
error:&error]; |
|
|
|
|
if (factory == nil) { |
|
|
|
|
NSLog(@"Error creating secure channel factory: %@", error); |
|
|
|
|
} |
|
|
|
|
return factory; |
|
|
|
|
#ifdef GRPC_COMPILE_WITH_CRONET |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
// fallthrough |
|
|
|
|
case GRPCTransportTypeCronet: |
|
|
|
|
return [GRPCCronetChannelFactory sharedInstance]; |
|
|
|
|
case GRPCTransportTypeInsecure: |
|
|
|
|
return [GRPCInsecureChannelFactory sharedInstance]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Add call ref count to the channel and maybe reset the timer. */ |
|
|
|
|
- (void)refChannel; |
|
|
|
|
- (NSDictionary *)channelArgs { |
|
|
|
|
NSMutableDictionary *args = [NSMutableDictionary new]; |
|
|
|
|
|
|
|
|
|
/** Reduce call ref count to the channel and maybe set the timer. */ |
|
|
|
|
- (void)unrefChannel; |
|
|
|
|
NSString *userAgent = @"grpc-objc/" GRPC_OBJC_VERSION_STRING; |
|
|
|
|
NSString *userAgentPrefix = _callOptions.userAgentPrefix; |
|
|
|
|
if (userAgentPrefix) { |
|
|
|
|
args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = |
|
|
|
|
[_callOptions.userAgentPrefix stringByAppendingFormat:@" %@", userAgent]; |
|
|
|
|
} else { |
|
|
|
|
args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = userAgent; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Disconnect the channel. Any further ref/unref are discarded. */ |
|
|
|
|
- (void)disconnect; |
|
|
|
|
NSString *hostNameOverride = _callOptions.hostNameOverride; |
|
|
|
|
if (hostNameOverride) { |
|
|
|
|
args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = hostNameOverride; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@end |
|
|
|
|
if (_callOptions.responseSizeLimit) { |
|
|
|
|
args[@GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH] = |
|
|
|
|
[NSNumber numberWithUnsignedInteger:_callOptions.responseSizeLimit]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@implementation GRPCChannelRef { |
|
|
|
|
NSTimeInterval _destroyDelay; |
|
|
|
|
void (^_destroyChannelCallback)(); |
|
|
|
|
if (_callOptions.compressionAlgorithm != GRPC_COMPRESS_NONE) { |
|
|
|
|
args[@GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM] = |
|
|
|
|
[NSNumber numberWithInt:_callOptions.compressionAlgorithm]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
NSUInteger _refCount; |
|
|
|
|
BOOL _disconnected; |
|
|
|
|
dispatch_queue_t _dispatchQueue; |
|
|
|
|
if (_callOptions.keepaliveInterval != 0) { |
|
|
|
|
args[@GRPC_ARG_KEEPALIVE_TIME_MS] = |
|
|
|
|
[NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.keepaliveInterval * 1000)]; |
|
|
|
|
args[@GRPC_ARG_KEEPALIVE_TIMEOUT_MS] = |
|
|
|
|
[NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.keepaliveTimeout * 1000)]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Date and time when last timer is scheduled. If a firing timer's scheduled date is different |
|
|
|
|
* from this, it is discarded. |
|
|
|
|
*/ |
|
|
|
|
NSDate *_lastDispatch; |
|
|
|
|
} |
|
|
|
|
if (_callOptions.retryEnabled == NO) { |
|
|
|
|
args[@GRPC_ARG_ENABLE_RETRIES] = [NSNumber numberWithInt:_callOptions.retryEnabled]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (instancetype)initWithDestroyDelay:(NSTimeInterval)destroyDelay |
|
|
|
|
destroyChannelCallback:(void (^)())destroyChannelCallback { |
|
|
|
|
if ((self = [super init])) { |
|
|
|
|
_destroyDelay = destroyDelay; |
|
|
|
|
_destroyChannelCallback = destroyChannelCallback; |
|
|
|
|
if (_callOptions.connectMinTimeout > 0) { |
|
|
|
|
args[@GRPC_ARG_MIN_RECONNECT_BACKOFF_MS] = |
|
|
|
|
[NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectMinTimeout * 1000)]; |
|
|
|
|
} |
|
|
|
|
if (_callOptions.connectInitialBackoff > 0) { |
|
|
|
|
args[@GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS] = [NSNumber |
|
|
|
|
numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectInitialBackoff * 1000)]; |
|
|
|
|
} |
|
|
|
|
if (_callOptions.connectMaxBackoff > 0) { |
|
|
|
|
args[@GRPC_ARG_MAX_RECONNECT_BACKOFF_MS] = |
|
|
|
|
[NSNumber numberWithUnsignedInteger:(NSUInteger)(_callOptions.connectMaxBackoff * 1000)]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_refCount = 1; |
|
|
|
|
_disconnected = NO; |
|
|
|
|
if (@available(iOS 8.0, *)) { |
|
|
|
|
_dispatchQueue = dispatch_queue_create( |
|
|
|
|
NULL, |
|
|
|
|
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, -1)); |
|
|
|
|
} else { |
|
|
|
|
_dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); |
|
|
|
|
} |
|
|
|
|
_lastDispatch = nil; |
|
|
|
|
if (_callOptions.logContext != nil) { |
|
|
|
|
args[@GRPC_ARG_MOBILE_LOG_CONTEXT] = _callOptions.logContext; |
|
|
|
|
} |
|
|
|
|
return self; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)refChannel { |
|
|
|
|
dispatch_async(_dispatchQueue, ^{ |
|
|
|
|
if (!self->_disconnected) { |
|
|
|
|
self->_refCount++; |
|
|
|
|
self->_lastDispatch = nil; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
if (_callOptions.channelPoolDomain.length != 0) { |
|
|
|
|
args[@GRPC_ARG_CHANNEL_POOL_DOMAIN] = _callOptions.channelPoolDomain; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[args addEntriesFromDictionary:_callOptions.additionalChannelArgs]; |
|
|
|
|
|
|
|
|
|
return args; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)unrefChannel { |
|
|
|
|
dispatch_async(_dispatchQueue, ^{ |
|
|
|
|
if (!self->_disconnected) { |
|
|
|
|
self->_refCount--; |
|
|
|
|
if (self->_refCount == 0) { |
|
|
|
|
NSDate *now = [NSDate date]; |
|
|
|
|
self->_lastDispatch = now; |
|
|
|
|
dispatch_time_t delay = |
|
|
|
|
dispatch_time(DISPATCH_TIME_NOW, (int64_t)self->_destroyDelay * NSEC_PER_SEC); |
|
|
|
|
dispatch_after(delay, self->_dispatchQueue, ^{ |
|
|
|
|
[self timedDisconnectWithScheduleDate:now]; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
- (nonnull id)copyWithZone:(nullable NSZone *)zone { |
|
|
|
|
GRPCChannelConfiguration *newConfig = |
|
|
|
|
[[GRPCChannelConfiguration alloc] initWithHost:_host callOptions:_callOptions]; |
|
|
|
|
|
|
|
|
|
return newConfig; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)disconnect { |
|
|
|
|
dispatch_async(_dispatchQueue, ^{ |
|
|
|
|
if (!self->_disconnected) { |
|
|
|
|
self->_lastDispatch = nil; |
|
|
|
|
self->_disconnected = YES; |
|
|
|
|
// Break retain loop |
|
|
|
|
self->_destroyChannelCallback = nil; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
- (BOOL)isEqual:(id)object { |
|
|
|
|
if (![object isKindOfClass:[GRPCChannelConfiguration class]]) { |
|
|
|
|
return NO; |
|
|
|
|
} |
|
|
|
|
GRPCChannelConfiguration *obj = (GRPCChannelConfiguration *)object; |
|
|
|
|
if (!(obj.host == _host || (_host != nil && [obj.host isEqualToString:_host]))) return NO; |
|
|
|
|
if (!(obj.callOptions == _callOptions || [obj.callOptions hasChannelOptionsEqualTo:_callOptions])) |
|
|
|
|
return NO; |
|
|
|
|
|
|
|
|
|
return YES; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)timedDisconnectWithScheduleDate:(NSDate *)scheduleDate { |
|
|
|
|
dispatch_async(_dispatchQueue, ^{ |
|
|
|
|
if (self->_disconnected || self->_lastDispatch != scheduleDate) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
self->_lastDispatch = nil; |
|
|
|
|
self->_disconnected = YES; |
|
|
|
|
self->_destroyChannelCallback(); |
|
|
|
|
// Break retain loop |
|
|
|
|
self->_destroyChannelCallback = nil; |
|
|
|
|
}); |
|
|
|
|
- (NSUInteger)hash { |
|
|
|
|
NSUInteger result = 0; |
|
|
|
|
result ^= _host.hash; |
|
|
|
|
result ^= _callOptions.channelOptionsHash; |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@end |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation GRPCChannel { |
|
|
|
|
GRPCChannelConfiguration *_configuration; |
|
|
|
|
grpc_channel *_unmanagedChannel; |
|
|
|
|
GRPCChannelRef *_channelRef; |
|
|
|
|
|
|
|
|
|
dispatch_queue_t _dispatchQueue; |
|
|
|
|
grpc_channel *_unmanagedChannel; |
|
|
|
|
NSTimeInterval _destroyDelay; |
|
|
|
|
|
|
|
|
|
NSUInteger _refcount; |
|
|
|
|
NSDate *_lastDispatch; |
|
|
|
|
} |
|
|
|
|
@synthesize disconnected = _disconnected; |
|
|
|
|
|
|
|
|
|
- (nullable instancetype)initWithChannelConfiguration:(GRPCChannelConfiguration *)channelConfiguration { |
|
|
|
|
return [self initWithChannelConfiguration:channelConfiguration |
|
|
|
|
destroyDelay:kDefaultChannelDestroyDelay]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (nullable instancetype)initWithChannelConfiguration:(GRPCChannelConfiguration *)channelConfiguration |
|
|
|
|
destroyDelay:(NSTimeInterval)destroyDelay { |
|
|
|
|
NSAssert(channelConfiguration, @"channelConfiguration must not be empty."); |
|
|
|
|
NSAssert(destroyDelay > 0, @"destroyDelay must be greater than 0."); |
|
|
|
|
if ((self = [super init])) { |
|
|
|
|
_configuration = [channelConfiguration copy]; |
|
|
|
|
if (@available(iOS 8.0, *)) { |
|
|
|
|
_dispatchQueue = dispatch_queue_create( |
|
|
|
|
NULL, |
|
|
|
|
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, -1)); |
|
|
|
|
} else { |
|
|
|
|
_dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create gRPC core channel object. |
|
|
|
|
NSString *host = channelConfiguration.host; |
|
|
|
|
NSAssert(host.length != 0, @"host cannot be nil"); |
|
|
|
|
NSDictionary *channelArgs; |
|
|
|
|
if (channelConfiguration.callOptions.additionalChannelArgs.count != 0) { |
|
|
|
|
NSMutableDictionary *args = [channelConfiguration.channelArgs mutableCopy]; |
|
|
|
|
[args addEntriesFromDictionary:channelConfiguration.callOptions.additionalChannelArgs]; |
|
|
|
|
channelArgs = args; |
|
|
|
|
} else { |
|
|
|
|
channelArgs = channelConfiguration.channelArgs; |
|
|
|
|
} |
|
|
|
|
id<GRPCChannelFactory> factory = channelConfiguration.channelFactory; |
|
|
|
|
_unmanagedChannel = [factory createChannelWithHost:host channelArgs:channelArgs]; |
|
|
|
|
if (_unmanagedChannel == NULL) { |
|
|
|
|
NSLog(@"Unable to create channel."); |
|
|
|
|
return nil; |
|
|
|
|
} |
|
|
|
|
_destroyDelay = destroyDelay; |
|
|
|
|
_disconnected = NO; |
|
|
|
|
} |
|
|
|
|
return self; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (grpc_call *)unmanagedCallWithPath:(NSString *)path |
|
|
|
|
completionQueue:(GRPCCompletionQueue *)queue |
|
|
|
|
callOptions:(GRPCCallOptions *)callOptions { |
|
|
|
|
callOptions:(GRPCCallOptions *)callOptions |
|
|
|
|
disconnected:(BOOL *)disconnected { |
|
|
|
|
NSAssert(path.length, @"path must not be empty."); |
|
|
|
|
NSAssert(queue, @"completionQueue must not be empty."); |
|
|
|
|
NSAssert(callOptions, @"callOptions must not be empty."); |
|
|
|
|
__block grpc_call *call = nil; |
|
|
|
|
__block BOOL isDisconnected = NO; |
|
|
|
|
__block grpc_call *call = NULL; |
|
|
|
|
dispatch_sync(_dispatchQueue, ^{ |
|
|
|
|
if (self->_unmanagedChannel) { |
|
|
|
|
if (self->_disconnected) { |
|
|
|
|
isDisconnected = YES; |
|
|
|
|
} else { |
|
|
|
|
NSAssert(self->_unmanagedChannel != NULL, @"Invalid channel."); |
|
|
|
|
|
|
|
|
|
NSString *serverAuthority = |
|
|
|
|
callOptions.transportType == GRPCTransportTypeCronet ? nil : callOptions.serverAuthority; |
|
|
|
|
callOptions.transportType == GRPCTransportTypeCronet ? nil : callOptions.serverAuthority; |
|
|
|
|
NSTimeInterval timeout = callOptions.timeout; |
|
|
|
|
NSAssert(timeout >= 0, @"Invalid timeout"); |
|
|
|
|
grpc_slice host_slice = grpc_empty_slice(); |
|
|
|
@ -171,10 +252,10 @@ static GRPCChannelPool *gChannelPool; |
|
|
|
|
} |
|
|
|
|
grpc_slice path_slice = grpc_slice_from_copied_string(path.UTF8String); |
|
|
|
|
gpr_timespec deadline_ms = |
|
|
|
|
timeout == 0 |
|
|
|
|
? gpr_inf_future(GPR_CLOCK_REALTIME) |
|
|
|
|
: gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), |
|
|
|
|
gpr_time_from_millis((int64_t)(timeout * 1000), GPR_TIMESPAN)); |
|
|
|
|
timeout == 0 |
|
|
|
|
? gpr_inf_future(GPR_CLOCK_REALTIME) |
|
|
|
|
: gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), |
|
|
|
|
gpr_time_from_millis((int64_t)(timeout * 1000), GPR_TIMESPAN)); |
|
|
|
|
call = grpc_channel_create_call(self->_unmanagedChannel, NULL, GRPC_PROPAGATE_DEFAULTS, |
|
|
|
|
queue.unmanagedQueue, path_slice, |
|
|
|
|
serverAuthority ? &host_slice : NULL, deadline_ms, NULL); |
|
|
|
@ -182,71 +263,64 @@ static GRPCChannelPool *gChannelPool; |
|
|
|
|
grpc_slice_unref(host_slice); |
|
|
|
|
} |
|
|
|
|
grpc_slice_unref(path_slice); |
|
|
|
|
} else { |
|
|
|
|
NSAssert(self->_unmanagedChannel != nil, @"Invalid channeg."); |
|
|
|
|
if (call == NULL) { |
|
|
|
|
NSLog(@"Unable to create call."); |
|
|
|
|
} else { |
|
|
|
|
// Ref the channel; |
|
|
|
|
[self ref]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
if (disconnected != nil) { |
|
|
|
|
*disconnected = isDisconnected; |
|
|
|
|
} |
|
|
|
|
return call; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This function should be called on _dispatchQueue. |
|
|
|
|
- (void)ref { |
|
|
|
|
dispatch_async(_dispatchQueue, ^{ |
|
|
|
|
if (self->_unmanagedChannel) { |
|
|
|
|
[self->_channelRef refChannel]; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
_refcount++; |
|
|
|
|
if (_refcount == 1 && _lastDispatch != nil) { |
|
|
|
|
_lastDispatch = nil; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)unref { |
|
|
|
|
dispatch_async(_dispatchQueue, ^{ |
|
|
|
|
if (self->_unmanagedChannel) { |
|
|
|
|
[self->_channelRef unrefChannel]; |
|
|
|
|
self->_refcount--; |
|
|
|
|
if (self->_refcount == 0 && !self->_disconnected) { |
|
|
|
|
// Start timer. |
|
|
|
|
dispatch_time_t delay = |
|
|
|
|
dispatch_time(DISPATCH_TIME_NOW, (int64_t)self->_destroyDelay * NSEC_PER_SEC); |
|
|
|
|
NSDate *now = [NSDate date]; |
|
|
|
|
self->_lastDispatch = now; |
|
|
|
|
dispatch_after(delay, self->_dispatchQueue, ^{ |
|
|
|
|
if (self->_lastDispatch == now) { |
|
|
|
|
grpc_channel_destroy(self->_unmanagedChannel); |
|
|
|
|
self->_unmanagedChannel = NULL; |
|
|
|
|
self->_disconnected = YES; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)disconnect { |
|
|
|
|
dispatch_async(_dispatchQueue, ^{ |
|
|
|
|
if (self->_unmanagedChannel) { |
|
|
|
|
if (!self->_disconnected) { |
|
|
|
|
grpc_channel_destroy(self->_unmanagedChannel); |
|
|
|
|
self->_unmanagedChannel = nil; |
|
|
|
|
[self->_channelRef disconnect]; |
|
|
|
|
self->_disconnected = YES; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)destroyChannel { |
|
|
|
|
dispatch_async(_dispatchQueue, ^{ |
|
|
|
|
if (self->_unmanagedChannel) { |
|
|
|
|
grpc_channel_destroy(self->_unmanagedChannel); |
|
|
|
|
self->_unmanagedChannel = nil; |
|
|
|
|
[gChannelPool removeChannel:self]; |
|
|
|
|
} |
|
|
|
|
- (BOOL)disconnected { |
|
|
|
|
__block BOOL disconnected; |
|
|
|
|
dispatch_sync(_dispatchQueue, ^{ |
|
|
|
|
disconnected = self->_disconnected; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (nullable instancetype)initWithUnmanagedChannel:(grpc_channel *_Nullable)unmanagedChannel |
|
|
|
|
configuration:(GRPCChannelConfiguration *)configuration { |
|
|
|
|
NSAssert(configuration, @"Configuration must not be empty."); |
|
|
|
|
if (!unmanagedChannel) { |
|
|
|
|
return nil; |
|
|
|
|
} |
|
|
|
|
if ((self = [super init])) { |
|
|
|
|
_unmanagedChannel = unmanagedChannel; |
|
|
|
|
_configuration = [configuration copy]; |
|
|
|
|
_channelRef = [[GRPCChannelRef alloc] initWithDestroyDelay:kChannelDestroyDelay |
|
|
|
|
destroyChannelCallback:^{ |
|
|
|
|
[self destroyChannel]; |
|
|
|
|
}]; |
|
|
|
|
if (@available(iOS 8.0, *)) { |
|
|
|
|
_dispatchQueue = dispatch_queue_create( |
|
|
|
|
NULL, |
|
|
|
|
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, -1)); |
|
|
|
|
} else { |
|
|
|
|
_dispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return self; |
|
|
|
|
return disconnected; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)dealloc { |
|
|
|
@ -255,47 +329,4 @@ static GRPCChannelPool *gChannelPool; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+ (nullable instancetype)createChannelWithConfiguration:(GRPCChannelConfiguration *)config { |
|
|
|
|
NSAssert(config != nil, @"configuration cannot be empty"); |
|
|
|
|
NSString *host = config.host; |
|
|
|
|
NSAssert(host.length != 0, @"host cannot be nil"); |
|
|
|
|
|
|
|
|
|
NSDictionary *channelArgs; |
|
|
|
|
if (config.callOptions.additionalChannelArgs.count != 0) { |
|
|
|
|
NSMutableDictionary *args = [config.channelArgs mutableCopy]; |
|
|
|
|
[args addEntriesFromDictionary:config.callOptions.additionalChannelArgs]; |
|
|
|
|
channelArgs = args; |
|
|
|
|
} else { |
|
|
|
|
channelArgs = config.channelArgs; |
|
|
|
|
} |
|
|
|
|
id<GRPCChannelFactory> factory = config.channelFactory; |
|
|
|
|
grpc_channel *unmanaged_channel = [factory createChannelWithHost:host channelArgs:channelArgs]; |
|
|
|
|
return [[GRPCChannel alloc] initWithUnmanagedChannel:unmanaged_channel configuration:config]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+ (nullable instancetype)channelWithHost:(NSString *)host |
|
|
|
|
callOptions:(GRPCCallOptions *)callOptions { |
|
|
|
|
static dispatch_once_t initChannelPool; |
|
|
|
|
dispatch_once(&initChannelPool, ^{ |
|
|
|
|
gChannelPool = [[GRPCChannelPool alloc] init]; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
NSURL *hostURL = [NSURL URLWithString:[@"https://" stringByAppendingString:host]]; |
|
|
|
|
if (hostURL.host && !hostURL.port) { |
|
|
|
|
host = [hostURL.host stringByAppendingString:@":443"]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
GRPCChannelConfiguration *channelConfig = |
|
|
|
|
[[GRPCChannelConfiguration alloc] initWithHost:host callOptions:callOptions]; |
|
|
|
|
if (channelConfig == nil) { |
|
|
|
|
return nil; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return [gChannelPool channelWithConfiguration:channelConfig]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
+ (void)closeOpenConnections { |
|
|
|
|
[gChannelPool removeAndCloseAllChannels]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@end |
|
|
|
|