Skip to content
Snippets Groups Projects
Commit 7e6b2da5 authored by Jan Tattermusch's avatar Jan Tattermusch
Browse files

Merge pull request #5720 from jcanizales/reachability-objc-fix

Reachability ObjC fix
parents 0561cd89 8b414f18
No related branches found
No related tags found
No related merge requests found
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#include <grpc/support/time.h> #include <grpc/support/time.h>
#import <RxLibrary/GRXConcurrentWriteable.h> #import <RxLibrary/GRXConcurrentWriteable.h>
#import "private/GRPCConnectivityMonitor.h"
#import "private/GRPCHost.h"
#import "private/GRPCRequestHeaders.h" #import "private/GRPCRequestHeaders.h"
#import "private/GRPCWrappedCall.h" #import "private/GRPCWrappedCall.h"
#import "private/NSData+GRPC.h" #import "private/NSData+GRPC.h"
...@@ -71,8 +73,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; ...@@ -71,8 +73,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
@implementation GRPCCall { @implementation GRPCCall {
dispatch_queue_t _callQueue; dispatch_queue_t _callQueue;
NSString *_host;
NSString *_path;
GRPCWrappedCall *_wrappedCall; GRPCWrappedCall *_wrappedCall;
dispatch_once_t _callAlreadyInvoked; dispatch_once_t _callAlreadyInvoked;
GRPCConnectivityMonitor *_connectivityMonitor;
// The C gRPC library has less guarantees on the ordering of events than we // The C gRPC library has less guarantees on the ordering of events than we
// do. Particularly, in the face of errors, there's no ordering guarantee at // do. Particularly, in the face of errors, there's no ordering guarantee at
...@@ -115,13 +120,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; ...@@ -115,13 +120,11 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
format:@"The requests writer can't be already started."]; format:@"The requests writer can't be already started."];
} }
if ((self = [super init])) { if ((self = [super init])) {
_wrappedCall = [[GRPCWrappedCall alloc] initWithHost:host path:path]; _host = [host copy];
if (!_wrappedCall) { _path = [path copy];
return nil;
}
// Serial queue to invoke the non-reentrant methods of the grpc_call object. // Serial queue to invoke the non-reentrant methods of the grpc_call object.
_callQueue = dispatch_queue_create("org.grpc.call", NULL); _callQueue = dispatch_queue_create("io.grpc.call", NULL);
_requestWriter = requestWriter; _requestWriter = requestWriter;
...@@ -156,7 +159,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; ...@@ -156,7 +159,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
- (void)cancel { - (void)cancel {
[self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeCancelled code:GRPCErrorCodeCancelled
userInfo:nil]]; userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]];
[self cancelCall]; [self cancelCall];
} }
...@@ -354,8 +357,29 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; ...@@ -354,8 +357,29 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
_retainSelf = self; _retainSelf = self;
_responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable]; _responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable];
_wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host path:_path];
NSAssert(_wrappedCall, @"Error allocating RPC objects. Low memory?");
[self sendHeaders:_requestHeaders]; [self sendHeaders:_requestHeaders];
[self invokeCall]; [self invokeCall];
// TODO(jcanizales): Extract this logic somewhere common.
NSString *host = [NSURL URLWithString:[@"https://" stringByAppendingString:_host]].host;
if (!host) {
// TODO(jcanizales): Check this on init.
[NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host];
}
__weak typeof(self) weakSelf = self;
_connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host];
[_connectivityMonitor handleLossWithHandler:^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain
code:GRPCErrorCodeUnavailable
userInfo:@{NSLocalizedDescriptionKey: @"Connectivity lost."}]];
[[GRPCHost hostWithAddress:strongSelf->_host] disconnect];
}
}];
} }
- (void)setState:(GRXWriterState)newState { - (void)setState:(GRXWriterState)newState {
...@@ -385,4 +409,5 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; ...@@ -385,4 +409,5 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey";
return; return;
} }
} }
@end @end
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <grpc/grpc.h> #include <grpc/grpc.h>
@class GRPCCompletionQueue;
struct grpc_channel_credentials; struct grpc_channel_credentials;
...@@ -80,4 +81,6 @@ struct grpc_channel_credentials; ...@@ -80,4 +81,6 @@ struct grpc_channel_credentials;
+ (nonnull GRPCChannel *)insecureChannelWithHost:(nonnull NSString *)host + (nonnull GRPCChannel *)insecureChannelWithHost:(nonnull NSString *)host
channelArgs:(nullable NSDictionary *)channelArgs; channelArgs:(nullable NSDictionary *)channelArgs;
- (nullable grpc_call *)unmanagedCallWithPath:(nonnull NSString *)path
completionQueue:(nonnull GRPCCompletionQueue *)queue;
@end @end
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/string_util.h> #include <grpc/support/string_util.h>
#import "GRPCCompletionQueue.h"
/** /**
* Returns @c grpc_channel_credentials from the specified @c path. If the file at the path could not * Returns @c grpc_channel_credentials from the specified @c path. If the file at the path could not
* be read then NULL is returned. If NULL is returned, @c errorPtr may not be NULL if there are * be read then NULL is returned. If NULL is returned, @c errorPtr may not be NULL if there are
...@@ -205,4 +207,16 @@ grpc_channel_args * buildChannelArgs(NSDictionary *dictionary) { ...@@ -205,4 +207,16 @@ grpc_channel_args * buildChannelArgs(NSDictionary *dictionary) {
channelArgs:channelArgs]; channelArgs:channelArgs];
} }
- (grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue {
return grpc_channel_create_call(_unmanagedChannel,
NULL, GRPC_PROPAGATE_DEFAULTS,
queue.unmanagedQueue,
path.UTF8String,
// Get "host" from "host:port"
// TODO(jcanizales): Use NSURLs throughout, to clarify these.
[_host componentsSeparatedByString:@":"][0].UTF8String,
gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
}
@end @end
/*
*
* 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 every 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
/*
*
* 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 a GRPCReachabilityFlags object, and passes the
// received ones to it.
static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target,
SCNetworkReachabilityFlags flags,
void *info) {
#pragma unused (target)
// This can be called many times with the same info. The info is retained by SCNetworkReachability
// while this function is being executed.
void (^handler)(GRPCReachabilityFlags *) = (__bridge void (^)(GRPCReachabilityFlags *))info;
handler([[GRPCReachabilityFlags alloc] initWithFlags: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 {
[self startListeningWithHandler:^(GRPCReachabilityFlags *flags) {
if (!flags.isHostReachable) {
handler();
}
}];
}
- (void)startListeningWithHandler:(void (^)(GRPCReachabilityFlags *))handler {
// Copy to ensure the handler block is in the heap (and so can't be deallocated when this method
// returns).
void (^copiedHandler)(GRPCReachabilityFlags *) = [handler copy];
SCNetworkReachabilityContext context = {
.version = 0,
.info = (__bridge void *)copiedHandler,
.retain = CFRetain,
.release = CFRelease,
};
// The following will retain context.info, and release it when the callback is set to NULL.
SCNetworkReachabilitySetCallback(_reachabilityRef, PassFlagsToContextInfoBlock, &context);
SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _queue);
}
- (void)stopListening {
// This releases the block on context.info.
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
...@@ -33,27 +33,39 @@ ...@@ -33,27 +33,39 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class GRPCCompletionQueue; @class GRPCCompletionQueue;
struct grpc_call; struct grpc_call;
@interface GRPCHost : NSObject @interface GRPCHost : NSObject
@property(nonatomic, readonly) NSString *address; @property(nonatomic, readonly) NSString *address;
@property(nonatomic, copy) NSString *userAgentPrefix; @property(nonatomic, copy, nullable) NSString *userAgentPrefix;
/** The following properties should only be modified for testing: */ /** The following properties should only be modified for testing: */
@property(nonatomic, getter=isSecure) BOOL secure; @property(nonatomic, getter=isSecure) BOOL secure;
@property(nonatomic, copy) NSString *pathToCertificates; @property(nonatomic, copy, nullable) NSString *pathToCertificates;
@property(nonatomic, copy) NSString *hostNameOverride; @property(nonatomic, copy, nullable) NSString *hostNameOverride;
- (nullable instancetype)init NS_UNAVAILABLE;
/** Host objects initialized with the same address are the same. */ /** Host objects initialized with the same address are the same. */
+ (instancetype)hostWithAddress:(NSString *)address; + (nullable instancetype)hostWithAddress:(NSString *)address;
- (instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER; - (nullable instancetype)initWithAddress:(NSString *)address NS_DESIGNATED_INITIALIZER;
/** Create a grpc_call object to the provided path on this host. */ /** Create a grpc_call object to the provided path on this host. */
- (struct grpc_call *)unmanagedCallWithPath:(NSString *)path - (nullable struct grpc_call *)unmanagedCallWithPath:(NSString *)path
completionQueue:(GRPCCompletionQueue *)queue; completionQueue:(GRPCCompletionQueue *)queue;
// TODO: There's a race when a new RPC is coming through just as an existing one is getting
// notified that there's no connectivity. If connectivity comes back at that moment, the new RPC
// will have its channel destroyed by the other RPC, and will never get notified of a problem, so
// it'll hang (the C layer logs a timeout, with exponential back off). One solution could be to pass
// the GRPCChannel to the GRPCCall, renaming this as "disconnectChannel:channel", which would only
// act on that specific channel.
- (void)disconnect;
@end @end
NS_ASSUME_NONNULL_END
...@@ -34,33 +34,30 @@ ...@@ -34,33 +34,30 @@
#import "GRPCHost.h" #import "GRPCHost.h"
#include <grpc/grpc.h> #include <grpc/grpc.h>
#import <GRPCClient/GRPCCall.h>
#import <GRPCClient/GRPCCall+ChannelArg.h> #import <GRPCClient/GRPCCall+ChannelArg.h>
#import "GRPCChannel.h" #import "GRPCChannel.h"
#import "GRPCCompletionQueue.h" #import "GRPCCompletionQueue.h"
#import "NSDictionary+GRPC.h" #import "NSDictionary+GRPC.h"
NS_ASSUME_NONNULL_BEGIN
// TODO(jcanizales): Generate the version in a standalone header, from templates. Like // TODO(jcanizales): Generate the version in a standalone header, from templates. Like
// templates/src/core/surface/version.c.template . // templates/src/core/surface/version.c.template .
#define GRPC_OBJC_VERSION_STRING @"0.13.0" #define GRPC_OBJC_VERSION_STRING @"0.13.0"
@interface GRPCHost () @implementation GRPCHost {
// TODO(mlumish): Investigate whether caching channels with strong links is a good idea. // TODO(mlumish): Investigate whether caching channels with strong links is a good idea.
@property(nonatomic, strong) GRPCChannel *channel; GRPCChannel *_channel;
@end
@implementation GRPCHost
+ (instancetype)hostWithAddress:(NSString *)address {
return [[self alloc] initWithAddress:address];
} }
- (instancetype)init { + (nullable instancetype)hostWithAddress:(NSString *)address {
return [self initWithAddress:nil]; return [[self alloc] initWithAddress:address];
} }
// Default initializer. // Default initializer.
- (instancetype)initWithAddress:(NSString *)address { - (nullable instancetype)initWithAddress:(NSString *)address {
if (!address) { if (!address) {
return nil; return nil;
} }
...@@ -95,46 +92,45 @@ ...@@ -95,46 +92,45 @@
return self; return self;
} }
- (grpc_call *)unmanagedCallWithPath:(NSString *)path completionQueue:(GRPCCompletionQueue *)queue { - (nullable grpc_call *)unmanagedCallWithPath:(NSString *)path
if (!queue || !path || !self.channel) { completionQueue:(GRPCCompletionQueue *)queue {
return NULL; GRPCChannel *channel;
// This is racing -[GRPCHost disconnect].
@synchronized(self) {
if (!_channel) {
_channel = [self newChannel];
}
channel = _channel;
} }
return grpc_channel_create_call(self.channel.unmanagedChannel, return [channel unmanagedCallWithPath:path completionQueue:queue];
NULL, GRPC_PROPAGATE_DEFAULTS,
queue.unmanagedQueue,
path.UTF8String,
self.hostName.UTF8String,
gpr_inf_future(GPR_CLOCK_REALTIME), NULL);
} }
- (GRPCChannel *)channel { - (NSDictionary *)channelArgs {
// Create it lazily, because we don't want to open a connection just because someone is NSMutableDictionary *args = [NSMutableDictionary dictionary];
// configuring a host.
if (!_channel) { // TODO(jcanizales): Add OS and device information (see
NSMutableDictionary *args = [NSMutableDictionary dictionary]; // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents ).
NSString *userAgent = @"grpc-objc/" GRPC_OBJC_VERSION_STRING;
if (_userAgentPrefix) {
userAgent = [_userAgentPrefix stringByAppendingFormat:@" %@", userAgent];
}
args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = userAgent;
// TODO(jcanizales): Add OS and device information (see if (_secure && _hostNameOverride) {
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents ). args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = _hostNameOverride;
NSString *userAgent = @"grpc-objc/" GRPC_OBJC_VERSION_STRING; }
if (_userAgentPrefix) { return args;
userAgent = [@[_userAgentPrefix, userAgent] componentsJoinedByString:@" "]; }
}
args[@GRPC_ARG_PRIMARY_USER_AGENT_STRING] = userAgent; - (GRPCChannel *)newChannel {
NSDictionary *args = [self channelArgs];
if (_secure) { if (_secure) {
if (_hostNameOverride) { return [GRPCChannel secureChannelWithHost:_address
args[@GRPC_SSL_TARGET_NAME_OVERRIDE_ARG] = _hostNameOverride; pathToCertificates:_pathToCertificates
} channelArgs:args];
} else {
_channel = [GRPCChannel secureChannelWithHost:_address return [GRPCChannel insecureChannelWithHost:_address channelArgs:args];
pathToCertificates:_pathToCertificates
channelArgs:args];
} else {
_channel = [GRPCChannel insecureChannelWithHost:_address channelArgs:args];
}
} }
return _channel;
} }
- (NSString *)hostName { - (NSString *)hostName {
...@@ -142,7 +138,16 @@ ...@@ -142,7 +138,16 @@
return _hostNameOverride ?: _address; return _hostNameOverride ?: _address;
} }
- (void)disconnect {
// This is racing -[GRPCHost unmanagedCallWithPath:completionQueue:].
@synchronized(self) {
_channel = nil;
}
}
// TODO(jcanizales): Don't let set |secure| to |NO| if |pathToCertificates| or |hostNameOverride| // TODO(jcanizales): Don't let set |secure| to |NO| if |pathToCertificates| or |hostNameOverride|
// have been set. Don't let set either of the latter if |secure| has been set to |NO|. // have been set. Don't let set either of the latter if |secure| has been set to |NO|.
@end @end
NS_ASSUME_NONNULL_END
/*
*
* 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)
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#import "GRPCChannel.h"
#import "GRPCRequestHeaders.h" #import "GRPCRequestHeaders.h"
@interface GRPCOperation : NSObject @interface GRPCOperation : NSObject
...@@ -94,4 +93,5 @@ ...@@ -94,4 +93,5 @@
- (void)startBatchWithOperations:(NSArray *)ops; - (void)startBatchWithOperations:(NSArray *)ops;
- (void)cancel; - (void)cancel;
@end @end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment