Move C-layer call creation into GRPCChannel, so that it always acts on a non-destroyed channel. Listen for connectivity in the GRPCCall, and destroy the channel.pull/5720/head
parent
180ca86350
commit
f1d084a4d1
9 changed files with 445 additions and 60 deletions
@ -0,0 +1,77 @@ |
||||
/*
|
||||
* |
||||
* 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 <Foundation/Foundation.h> |
||||
#import <SystemConfiguration/SystemConfiguration.h> |
||||
|
||||
@interface GRPCReachabilityFlags : NSObject |
||||
|
||||
+ (nonnull instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags; |
||||
|
||||
/**
|
||||
* One accessor method to query each of the different flags. Example: |
||||
|
||||
@property(nonatomic, readonly) BOOL isCell; |
||||
|
||||
*/ |
||||
#define GRPC_XMACRO_ITEM(methodName, FlagName) \ |
||||
@property(nonatomic, readonly) BOOL methodName; |
||||
|
||||
#include "GRPCReachabilityFlagNames.xmacro.h" |
||||
#undef GRPC_XMACRO_ITEM |
||||
|
||||
@property(nonatomic, readonly) BOOL isHostReachable; |
||||
@end |
||||
|
||||
|
||||
@interface GRPCConnectivityMonitor : NSObject |
||||
|
||||
+ (nullable instancetype)monitorWithHost:(nonnull NSString *)hostName; |
||||
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE; |
||||
|
||||
/**
|
||||
* Queue on which callbacks will be dispatched. Default is the main queue. Set it before calling |
||||
* handleLossWithHandler:. |
||||
*/ |
||||
// TODO(jcanizales): Default to a serial background queue instead.
|
||||
@property(nonatomic, strong, null_resettable) dispatch_queue_t queue; |
||||
|
||||
/**
|
||||
* Calls handler the next time the connectivity to this instance's host is lost. If this instance is |
||||
* released before that happens, the handler won't be called. |
||||
* Only one handler is active at a time, so if this method is called again before the previous |
||||
* handler has been called, it might never be called at all (or yes, if it has already been queued). |
||||
*/ |
||||
- (void)handleLossWithHandler:(nonnull void (^)())handler; |
||||
@end |
@ -0,0 +1,188 @@ |
||||
/* |
||||
* |
||||
* 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 "GRPCConnectivityMonitor.h" |
||||
|
||||
#pragma mark Flags |
||||
|
||||
@implementation GRPCReachabilityFlags { |
||||
SCNetworkReachabilityFlags _flags; |
||||
} |
||||
|
||||
+ (instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags { |
||||
return [[self alloc] initWithFlags:flags]; |
||||
} |
||||
|
||||
- (instancetype)initWithFlags:(SCNetworkReachabilityFlags)flags { |
||||
if ((self = [super init])) { |
||||
_flags = flags; |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
/* |
||||
* One accessor method implementation per flag. Example: |
||||
|
||||
- (BOOL)isCell { \ |
||||
return !!(_flags & kSCNetworkReachabilityFlagsIsWWAN); \ |
||||
} |
||||
|
||||
*/ |
||||
#define GRPC_XMACRO_ITEM(methodName, FlagName) \ |
||||
- (BOOL)methodName { \ |
||||
return !!(_flags & kSCNetworkReachabilityFlags ## FlagName); \ |
||||
} |
||||
#include "GRPCReachabilityFlagNames.xmacro.h" |
||||
#undef GRPC_XMACRO_ITEM |
||||
|
||||
- (BOOL)isHostReachable { |
||||
// Note: connectionOnDemand means it'll be reachable only if using the CFSocketStream API or APIs |
||||
// on top of it. |
||||
// connectionRequired means we can't tell until a connection is attempted (e.g. for VPN on |
||||
// demand). |
||||
return self.reachable && !self.interventionRequired && !self.connectionOnDemand; |
||||
} |
||||
|
||||
- (NSString *)description { |
||||
NSMutableArray *activeOptions = [NSMutableArray arrayWithCapacity:9]; |
||||
|
||||
/* |
||||
* For each flag, add its name to the array if it's ON. Example: |
||||
|
||||
if (self.isCell) { |
||||
[activeOptions addObject:@"isCell"]; |
||||
} |
||||
|
||||
*/ |
||||
#define GRPC_XMACRO_ITEM(methodName, FlagName) \ |
||||
if (self.methodName) { \ |
||||
[activeOptions addObject:@#methodName]; \ |
||||
} |
||||
#include "GRPCReachabilityFlagNames.xmacro.h" |
||||
#undef GRPC_XMACRO_ITEM |
||||
|
||||
return activeOptions.count == 0 ? @"(none)" : [activeOptions componentsJoinedByString:@", "]; |
||||
} |
||||
|
||||
- (BOOL)isEqual:(id)object { |
||||
return [object isKindOfClass:[GRPCReachabilityFlags class]] && |
||||
_flags == ((GRPCReachabilityFlags *)object)->_flags; |
||||
} |
||||
|
||||
- (NSUInteger)hash { |
||||
return _flags; |
||||
} |
||||
@end |
||||
|
||||
#pragma mark Connectivity Monitor |
||||
|
||||
// Assumes the third argument is a block that accepts SCNetworkReachabilityFlags, and passes the |
||||
// received ones to it. |
||||
static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target, |
||||
SCNetworkReachabilityFlags flags, |
||||
void *info) { |
||||
#pragma unused (target) |
||||
((__bridge void (^)(SCNetworkReachabilityFlags))info)(flags); |
||||
} |
||||
|
||||
@implementation GRPCConnectivityMonitor { |
||||
SCNetworkReachabilityRef _reachabilityRef; |
||||
} |
||||
|
||||
- (nullable instancetype)initWithReachability:(nullable SCNetworkReachabilityRef)reachability { |
||||
if (!reachability) { |
||||
return nil; |
||||
} |
||||
if ((self = [super init])) { |
||||
_reachabilityRef = CFRetain(reachability); |
||||
_queue = dispatch_get_main_queue(); |
||||
} |
||||
return self; |
||||
} |
||||
|
||||
+ (nullable instancetype)monitorWithHost:(nonnull NSString *)host { |
||||
const char *hostName = host.UTF8String; |
||||
if (!hostName) { |
||||
[NSException raise:NSInvalidArgumentException |
||||
format:@"host.UTF8String returns NULL for %@", host]; |
||||
} |
||||
SCNetworkReachabilityRef reachability = |
||||
SCNetworkReachabilityCreateWithName(NULL, hostName); |
||||
|
||||
GRPCConnectivityMonitor *returnValue = [[self alloc] initWithReachability:reachability]; |
||||
if (reachability) { |
||||
CFRelease(reachability); |
||||
} |
||||
return returnValue; |
||||
} |
||||
|
||||
- (void)handleLossWithHandler:(void (^)())handler { |
||||
__weak typeof(self) weakSelf = self; |
||||
[self startListeningWithHandler:^(GRPCReachabilityFlags *flags) { |
||||
if (!flags.isHostReachable) { |
||||
[weakSelf stopListening]; |
||||
handler(); |
||||
} |
||||
}]; |
||||
} |
||||
|
||||
- (void)startListeningWithHandler:(void (^)(GRPCReachabilityFlags *))handler { |
||||
SCNetworkReachabilityContext context = { |
||||
.version = 0, |
||||
.info = (__bridge_retained void *)^(SCNetworkReachabilityFlags flags){ |
||||
// Pass the flags as as Objective-C wrapper. |
||||
handler([[GRPCReachabilityFlags alloc] initWithFlags:flags]); |
||||
}, |
||||
.release = CFRelease |
||||
}; |
||||
SCNetworkReachabilitySetCallback(_reachabilityRef, PassFlagsToContextInfoBlock, &context); |
||||
SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _queue); |
||||
} |
||||
|
||||
- (void)stopListening { |
||||
SCNetworkReachabilitySetCallback(_reachabilityRef, NULL, NULL); |
||||
SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, NULL); |
||||
} |
||||
|
||||
- (void)setQueue:(dispatch_queue_t)queue { |
||||
_queue = queue ?: dispatch_get_main_queue(); |
||||
} |
||||
|
||||
- (void)dealloc { |
||||
if (_reachabilityRef) { |
||||
[self stopListening]; |
||||
CFRelease(_reachabilityRef); |
||||
} |
||||
} |
||||
|
||||
@end |
@ -0,0 +1,65 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
/**
|
||||
* "X-macro" file that lists the flags names of Apple's Network Reachability API, along with a nice |
||||
* Objective-C method name used to query each of them. |
||||
* |
||||
* Example usage: To generate a dictionary from flag value to name, one can do: |
||||
|
||||
NSDictionary *flagNames = @{ |
||||
#define GRPC_XMACRO_ITEM(methodName, FlagName) \ |
||||
@(kSCNetworkReachabilityFlags ## FlagName): @#methodName, |
||||
#include "GRXReachabilityFlagNames.xmacro.h" |
||||
#undef GRPC_XMACRO_ITEM |
||||
}; |
||||
|
||||
XCTAssertEqualObjects(flagNames[@(kSCNetworkReachabilityFlagsIsWWAN)], @"isCell"); |
||||
|
||||
*/ |
||||
|
||||
#ifndef GRPC_XMACRO_ITEM |
||||
#error This file is to be used with the "X-macro" pattern: Please #define \ |
||||
GRPC_XMACRO_ITEM(methodName, FlagName), then #include this file, and then #undef \
|
||||
GRPC_XMACRO_ITEM. |
||||
#endif |
||||
|
||||
GRPC_XMACRO_ITEM(isCell, IsWWAN) |
||||
GRPC_XMACRO_ITEM(reachable, Reachable) |
||||
GRPC_XMACRO_ITEM(transientConnection, TransientConnection) |
||||
GRPC_XMACRO_ITEM(connectionRequired, ConnectionRequired) |
||||
GRPC_XMACRO_ITEM(connectionOnTraffic, ConnectionOnTraffic) |
||||
GRPC_XMACRO_ITEM(interventionRequired, InterventionRequired) |
||||
GRPC_XMACRO_ITEM(connectionOnDemand, ConnectionOnDemand) |
||||
GRPC_XMACRO_ITEM(isLocalAddress, IsLocalAddress) |
||||
GRPC_XMACRO_ITEM(isDirect, IsDirect) |
Loading…
Reference in new issue