diff options
Diffstat (limited to 'src/objective-c/GRPCClient')
-rw-r--r-- | src/objective-c/GRPCClient/GRPCCall+GID.h | 29 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/GRPCCall+GID.m | 28 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/GRPCCall+OAuth2.h | 15 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/GRPCCall+OAuth2.m | 11 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/GRPCCall.h | 7 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/GRPCCall.m | 74 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/private/GRPCChannel.h | 1 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/private/GRPCChannel.m | 12 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/private/GRPCHost.h | 1 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/private/GRPCHost.m | 6 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/private/GRPCRequestHeaders.m | 1 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/private/GRPCWrappedCall.h | 3 | ||||
-rw-r--r-- | src/objective-c/GRPCClient/private/GRPCWrappedCall.m | 12 |
13 files changed, 170 insertions, 30 deletions
diff --git a/src/objective-c/GRPCClient/GRPCCall+GID.h b/src/objective-c/GRPCClient/GRPCCall+GID.h new file mode 100644 index 0000000000..3ee732e79e --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+GID.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "GRPCCall.h" +#import "GRPCCall+OAuth2.h" + +#import <Google/SignIn.h> + +/** + * Extend GIDSignIn class to comply GRPCAuthorizationProtocol + */ +@interface GIDSignIn (GRPC) <GRPCAuthorizationProtocol> +- (void)getTokenWithHandler:(void (^)(NSString *token))hander; +@end diff --git a/src/objective-c/GRPCClient/GRPCCall+GID.m b/src/objective-c/GRPCClient/GRPCCall+GID.m new file mode 100644 index 0000000000..030737147b --- /dev/null +++ b/src/objective-c/GRPCClient/GRPCCall+GID.m @@ -0,0 +1,28 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import "GRPCCall+GID.h" + +@implementation GIDSignIn (GRPC) + +- (void)getTokenWithHandler:(void (^)(NSString *token))handler { + NSString *token = self.currentUser.authentication.accessToken; + handler(token); +} + +@end diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h index 65465e9523..adb1042aa0 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h @@ -18,6 +18,13 @@ #import "GRPCCall.h" +/** + * The protocol of an OAuth2 token object from which GRPCCall can acquire a token. + */ +@protocol GRPCAuthorizationProtocol +- (void)getTokenWithHandler:(void (^)(NSString *token))hander; +@end + /** Helpers for setting and reading headers compatible with OAuth2. */ @interface GRPCCall (OAuth2) @@ -33,4 +40,12 @@ /** Returns the value (if any) of the "www-authenticate" response header (the challenge header). */ @property(atomic, readonly) NSString *oauth2ChallengeHeader; +/** + * The authorization token object to be used when starting the call. If the value is set to nil, no + * oauth authentication will be used. + * + * If tokenProvider exists, it takes precedence over the token set by oauth2AccessToken. + */ +@property(atomic, strong) id<GRPCAuthorizationProtocol> tokenProvider; + @end diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m index eaa7465087..8451ebe870 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.m +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.m @@ -16,6 +16,8 @@ * */ +#import <objc/runtime.h> + #import "GRPCCall+OAuth2.h" static NSString * const kAuthorizationHeader = @"authorization"; @@ -23,6 +25,7 @@ static NSString * const kBearerPrefix = @"Bearer "; static NSString * const kChallengeHeader = @"www-authenticate"; @implementation GRPCCall (OAuth2) +@dynamic tokenProvider; - (NSString *)oauth2AccessToken { NSString *headerValue = self.requestHeaders[kAuthorizationHeader]; @@ -45,4 +48,12 @@ static NSString * const kChallengeHeader = @"www-authenticate"; return self.responseHeaders[kChallengeHeader]; } +- (void)setTokenProvider:(id<GRPCAuthorizationProtocol>)tokenProvider { + objc_setAssociatedObject(self, @selector(tokenProvider), tokenProvider, OBJC_ASSOCIATION_RETAIN); +} + +- (id<GRPCAuthorizationProtocol>)tokenProvider { + return objc_getAssociatedObject(self, @selector(tokenProvider)); +} + @end diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 4d90cfd384..df563ca36c 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -170,6 +170,13 @@ extern id const kGRPCTrailersKey; @property (atomic, copy, readwrite) NSString *serverName; /** + * The timeout for the RPC call in seconds. If set to 0, the call will not timeout. If set to + * positive, the gRPC call returns with status GRPCErrorCodeDeadlineExceeded if it is not completed + * within \a timeout seconds. A negative value is not allowed. + */ +@property NSTimeInterval timeout; + +/** * The container of the request headers of an RPC conforms to this protocol, which is a subset of * NSMutableDictionary's interface. It will become a NSMutableDictionary later on. * The keys of this container are the header names, which per the HTTP standard are case- diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index 872362419e..d6c3a3c165 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -18,6 +18,8 @@ #import "GRPCCall.h" +#import "GRPCCall+OAuth2.h" + #include <grpc/grpc.h> #include <grpc/support/time.h> #import <RxLibrary/GRXConcurrentWriteable.h> @@ -40,10 +42,14 @@ NSString * const kGRPCHeadersKey = @"io.grpc.HeadersKey"; NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; static NSMutableDictionary *callFlags; +static NSString * const kAuthorizationHeader = @"authorization"; +static NSString * const kBearerPrefix = @"Bearer "; + @interface GRPCCall () <GRXWriteable> // Make them read-write. @property(atomic, strong) NSDictionary *responseHeaders; @property(atomic, strong) NSDictionary *responseTrailers; +@property(atomic) BOOL isWaitingForToken; @end // The following methods of a C gRPC call object aren't reentrant, and thus @@ -181,9 +187,6 @@ static NSMutableDictionary *callFlags; - (void)finishWithError:(NSError *)errorOrNil { @synchronized(self) { - if (_state == GRXWriterStateFinished) { - return; - } _state = GRXWriterStateFinished; } @@ -211,7 +214,11 @@ static NSMutableDictionary *callFlags; [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain code:GRPCErrorCodeCancelled userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]]; - [self cancelCall]; + if (!self.isWaitingForToken) { + [self cancelCall]; + } else { + self.isWaitingForToken = NO; + } } - (void)dealloc { @@ -410,22 +417,14 @@ static NSMutableDictionary *callFlags; #pragma mark GRXWriter implementation -- (void)startWithWriteable:(id<GRXWriteable>)writeable { - @synchronized(self) { - _state = GRXWriterStateStarted; - } - - // Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled). - // This makes RPCs in which the call isn't externally retained possible (as long as it is started - // before being autoreleased). - // Care is taken not to retain self strongly in any of the blocks used in this implementation, so - // that the life of the instance is determined by this retain cycle. - _retainSelf = self; - +- (void)startCallWithWriteable:(id<GRXWriteable>)writeable { _responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable dispatchQueue:_responseQueue]; - _wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host serverName:_serverName path:_path]; + _wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host + serverName:_serverName + path:_path + timeout:_timeout]; NSAssert(_wrappedCall, @"Error allocating RPC objects. Low memory?"); [self sendHeaders:_requestHeaders]; @@ -437,20 +436,49 @@ static NSMutableDictionary *callFlags; // TODO(jcanizales): Check this on init. [NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host]; } - __weak typeof(self) weakSelf = self; _connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host]; + __weak typeof(self) weakSelf = self; void (^handler)() = ^{ typeof(self) strongSelf = weakSelf; - if (strongSelf) { - [strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeUnavailable - userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]]; - } + [strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeUnavailable + userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]]; }; [_connectivityMonitor handleLossWithHandler:handler wifiStatusChangeHandler:nil]; } +- (void)startWithWriteable:(id<GRXWriteable>)writeable { + @synchronized(self) { + _state = GRXWriterStateStarted; + } + + // Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled). + // This makes RPCs in which the call isn't externally retained possible (as long as it is started + // before being autoreleased). + // Care is taken not to retain self strongly in any of the blocks used in this implementation, so + // that the life of the instance is determined by this retain cycle. + _retainSelf = self; + + if (self.tokenProvider != nil) { + self.isWaitingForToken = YES; + __weak typeof(self) weakSelf = self; + [self.tokenProvider getTokenWithHandler:^(NSString *token){ + typeof(self) strongSelf = weakSelf; + if (strongSelf && strongSelf.isWaitingForToken) { + if (token) { + NSString *t = [kBearerPrefix stringByAppendingString:token]; + strongSelf.requestHeaders[kAuthorizationHeader] = t; + } + [strongSelf startCallWithWriteable:writeable]; + strongSelf.isWaitingForToken = NO; + } + }]; + } else { + [self startCallWithWriteable:writeable]; + } +} + - (void)setState:(GRXWriterState)newState { @synchronized(self) { // Manual transitions are only allowed from the started or paused states. diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.h b/src/objective-c/GRPCClient/private/GRPCChannel.h index e2aa5bd036..d37182f754 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.h +++ b/src/objective-c/GRPCClient/private/GRPCChannel.h @@ -63,5 +63,6 @@ struct grpc_channel_credentials; - (nullable grpc_call *)unmanagedCallWithPath:(nonnull NSString *)path serverName:(nonnull NSString *)serverName + timeout:(NSTimeInterval)timeout completionQueue:(nonnull GRPCCompletionQueue *)queue; @end diff --git a/src/objective-c/GRPCClient/private/GRPCChannel.m b/src/objective-c/GRPCClient/private/GRPCChannel.m index 52dbc70b99..b78b14f2af 100644 --- a/src/objective-c/GRPCClient/private/GRPCChannel.m +++ b/src/objective-c/GRPCClient/private/GRPCChannel.m @@ -182,18 +182,28 @@ static grpc_channel_args *BuildChannelArgs(NSDictionary *dictionary) { - (grpc_call *)unmanagedCallWithPath:(NSString *)path serverName:(NSString *)serverName + timeout:(NSTimeInterval)timeout completionQueue:(GRPCCompletionQueue *)queue { + GPR_ASSERT(timeout >= 0); + if (timeout < 0) { + timeout = 0; + } grpc_slice host_slice; if (serverName) { host_slice = grpc_slice_from_copied_string(serverName.UTF8String); } 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)); grpc_call *call = grpc_channel_create_call(_unmanagedChannel, NULL, GRPC_PROPAGATE_DEFAULTS, queue.unmanagedQueue, path_slice, serverName ? &host_slice : NULL, - gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + deadline_ms, NULL); if (serverName) { grpc_slice_unref(host_slice); } diff --git a/src/objective-c/GRPCClient/private/GRPCHost.h b/src/objective-c/GRPCClient/private/GRPCHost.h index 0c1d715240..58171211b0 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.h +++ b/src/objective-c/GRPCClient/private/GRPCHost.h @@ -55,6 +55,7 @@ struct grpc_channel_credentials; /** Create a grpc_call object to the provided path on this host. */ - (nullable struct grpc_call *)unmanagedCallWithPath:(NSString *)path serverName:(NSString *)serverName + timeout:(NSTimeInterval)timeout completionQueue:(GRPCCompletionQueue *)queue; // TODO: There's a race when a new RPC is coming through just as an existing one is getting diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m index 23794c1fed..f73e9cbc50 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.m +++ b/src/objective-c/GRPCClient/private/GRPCHost.m @@ -121,6 +121,7 @@ static GRPCConnectivityMonitor *connectivityMonitor = nil; - (nullable grpc_call *)unmanagedCallWithPath:(NSString *)path serverName:(NSString *)serverName + timeout:(NSTimeInterval)timeout completionQueue:(GRPCCompletionQueue *)queue { GRPCChannel *channel; // This is racing -[GRPCHost disconnect]. @@ -130,7 +131,10 @@ static GRPCConnectivityMonitor *connectivityMonitor = nil; } channel = _channel; } - return [channel unmanagedCallWithPath:path serverName:serverName completionQueue:queue]; + return [channel unmanagedCallWithPath:path + serverName:serverName + timeout:timeout + completionQueue:queue]; } - (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCerts diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index 7640a64d6d..5de1d8fff5 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -103,7 +103,6 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { } - (void)setObject:(id)obj forKey:(NSString *)key { - [self checkCallIsNotStarted]; CheckIsNonNilASCII(@"Header name", key); key = key.lowercaseString; CheckKeyValuePairIsValid(key, obj); diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h index 64075591a3..1cd9da8f3e 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -76,7 +76,8 @@ - (instancetype)initWithHost:(NSString *)host serverName:(NSString *)serverName - path:(NSString *)path NS_DESIGNATED_INITIALIZER; + path:(NSString *)path + timeout:(NSTimeInterval)timeout NS_DESIGNATED_INITIALIZER; - (void)startBatchWithOperations:(NSArray *)ops errorHandler:(void(^)())errorHandler; diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index 9802465001..b0b1223b64 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -68,6 +68,8 @@ _op.data.send_initial_metadata.metadata = metadata.grpc_metadataArray; _op.data.send_initial_metadata.maybe_compression_level.is_set = false; _op.data.send_initial_metadata.maybe_compression_level.level = 0; + _op.data.send_initial_metadata.maybe_stream_compression_level.is_set = false; + _op.data.send_initial_metadata.maybe_stream_compression_level.level = 0; _op.flags = flags; _handler = handler; } @@ -236,12 +238,13 @@ } - (instancetype)init { - return [self initWithHost:nil serverName:nil path:nil]; + return [self initWithHost:nil serverName:nil path:nil timeout:0]; } - (instancetype)initWithHost:(NSString *)host serverName:(NSString *)serverName - path:(NSString *)path { + path:(NSString *)path + timeout:(NSTimeInterval)timeout { if (!path || !host) { [NSException raise:NSInvalidArgumentException format:@"path and host cannot be nil."]; @@ -253,7 +256,10 @@ // queue. Currently we use a singleton queue. _queue = [GRPCCompletionQueue completionQueue]; - _call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path serverName:serverName completionQueue:_queue]; + _call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path + serverName:serverName + timeout:timeout + completionQueue:_queue]; if (_call == NULL) { return nil; } |