123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- #import "RLMSyncSessionRefreshHandle.hpp"
- #import "RLMJSONModels.h"
- #import "RLMNetworkClient.h"
- #import "RLMSyncManager_Private.h"
- #import "RLMSyncUser_Private.hpp"
- #import "RLMSyncUtil_Private.hpp"
- #import "RLMUtil.hpp"
- #import "sync/sync_session.hpp"
- using namespace realm;
- namespace {
- void unregisterRefreshHandle(const std::weak_ptr<SyncUser>& user, const std::string& path) {
- if (auto strong_user = user.lock()) {
- context_for(strong_user).unregister_refresh_handle(path);
- }
- }
- void reportInvalidAccessToken(const std::weak_ptr<SyncUser>& user, NSError *error) {
- if (auto strong_user = user.lock()) {
- if (RLMUserErrorReportingBlock block = context_for(strong_user).error_handler()) {
- RLMSyncUser *theUser = [[RLMSyncUser alloc] initWithSyncUser:std::move(strong_user)];
- [theUser logOut];
- block(theUser, error);
- }
- }
- }
- }
- static const NSTimeInterval RLMRefreshBuffer = 10;
- @interface RLMSyncSessionRefreshHandle () {
- std::weak_ptr<SyncUser> _user;
- std::string _path;
- std::weak_ptr<SyncSession> _session;
- std::shared_ptr<SyncSession> _strongSession;
- }
- @property (nonatomic) dispatch_source_t timer;
- @property (nonatomic) NSURL *realmURL;
- @property (nonatomic) NSURL *authServerURL;
- @property (nonatomic, copy) RLMSyncBasicErrorReportingBlock completionBlock;
- @end
- @implementation RLMSyncSessionRefreshHandle
- - (instancetype)initWithRealmURL:(NSURL *)realmURL
- user:(std::shared_ptr<realm::SyncUser>)user
- session:(std::shared_ptr<realm::SyncSession>)session
- completionBlock:(RLMSyncBasicErrorReportingBlock)completionBlock {
- if (self = [super init]) {
- NSString *path = [realmURL path];
- _path = [path UTF8String];
- self.authServerURL = [NSURL URLWithString:@(user->server_url().c_str())];
- if (!self.authServerURL) {
- @throw RLMException(@"User object isn't configured with an auth server URL.");
- }
- self.completionBlock = completionBlock;
- self.realmURL = realmURL;
-
- _strongSession = std::move(session);
- _session = _strongSession;
- _user = user;
-
- [self _timerFired];
- return self;
- }
- return nil;
- }
- - (void)dealloc {
- [self cancelTimer];
- }
- - (void)invalidate {
- _strongSession = nullptr;
- [self cancelTimer];
- }
- - (void)cancelTimer {
- if (self.timer) {
- dispatch_source_cancel(self.timer);
- self.timer = nil;
- }
- }
- + (NSDate *)fireDateForTokenExpirationDate:(NSDate *)date nowDate:(NSDate *)nowDate {
- NSDate *fireDate = [date dateByAddingTimeInterval:-RLMRefreshBuffer];
-
- return ([fireDate compare:nowDate] == NSOrderedDescending ? fireDate : nil);
- }
- - (void)scheduleRefreshTimer:(NSDate *)dateWhenTokenExpires {
- [self cancelTimer];
- NSDate *fireDate = [RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:dateWhenTokenExpires
- nowDate:[NSDate date]];
- if (!fireDate) {
- unregisterRefreshHandle(_user, _path);
- return;
- }
- NSTimeInterval timeToExpiration = [fireDate timeIntervalSinceNow];
- self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
- dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, timeToExpiration * NSEC_PER_SEC),
- DISPATCH_TIME_FOREVER,
- NSEC_PER_SEC * (timeToExpiration / 10));
- dispatch_source_set_event_handler(self.timer, ^{ [self _timerFired]; });
- dispatch_resume(self.timer);
- }
- - (BOOL)_handleSuccessfulRequest:(RLMAuthResponseModel *)model session:(SyncSession&)session {
-
-
- if (model.urlPrefix) {
- session.set_url_prefix(model.urlPrefix.UTF8String);
- }
-
- NSString *resolvedURLString = nil;
- RLMServerPath resolvedPath = model.accessToken.tokenData.path;
-
- NSURLComponents *urlBuffer = [NSURLComponents componentsWithURL:self.realmURL
- resolvingAgainstBaseURL:YES];
- urlBuffer.path = resolvedPath;
- resolvedURLString = [[urlBuffer URL] absoluteString];
- if (!resolvedURLString) {
- @throw RLMException(@"Resolved path returned from the server was invalid (%@).", resolvedPath);
- }
-
- session.refresh_access_token([model.accessToken.token UTF8String], {resolvedURLString.UTF8String});
-
-
- _strongSession = nullptr;
- NSDate *expires = [NSDate dateWithTimeIntervalSince1970:model.accessToken.tokenData.expires];
- [self scheduleRefreshTimer:expires];
- if (self.completionBlock) {
- self.completionBlock(nil);
- }
- return true;
- }
- - (void)_handleFailedRequest:(NSError *)error {
- NSError *authError;
- if ([error.domain isEqualToString:RLMSyncAuthErrorDomain]) {
-
- authError = error;
-
- reportInvalidAccessToken(_user, authError);
- } else {
-
- authError = make_auth_error_bad_response();
- }
- if (self.completionBlock) {
- self.completionBlock(authError);
- }
- [[RLMSyncManager sharedManager] _fireError:make_sync_error(authError)];
-
- NSDate *nextTryDate = nil;
- if ([error.domain isEqualToString:NSURLErrorDomain]) {
- switch (error.code) {
- case NSURLErrorCannotConnectToHost:
- case NSURLErrorNotConnectedToInternet:
- case NSURLErrorNetworkConnectionLost:
- case NSURLErrorTimedOut:
- case NSURLErrorDNSLookupFailed:
- case NSURLErrorCannotFindHost:
-
- nextTryDate = [NSDate dateWithTimeIntervalSinceNow:RLMRefreshBuffer + 10];
- break;
- default:
- break;
- }
- }
- if (!nextTryDate) {
-
- if (_strongSession) {
- _strongSession->log_out();
- }
- unregisterRefreshHandle(_user, _path);
- [self invalidate];
- return;
- }
-
-
-
- _strongSession = nullptr;
- [self scheduleRefreshTimer:nextTryDate];
- return;
- }
- - (BOOL)_onRefreshCompletionWithError:(NSError *)error json:(NSDictionary *)json {
- std::shared_ptr<SyncSession> session = _session.lock();
- if (!session) {
-
- unregisterRefreshHandle(_user, _path);
- [self invalidate];
- return NO;
- }
- if (json && !error) {
- RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json
- requireAccessToken:YES
- requireRefreshToken:NO];
- if (model) {
- return [self _handleSuccessfulRequest:model session:*session];
- }
-
- unregisterRefreshHandle(_user, _path);
- NSError *error = make_sync_error(make_auth_error_bad_response(json));
- if (self.completionBlock) {
- self.completionBlock(error);
- }
- [[RLMSyncManager sharedManager] _fireError:error];
- } else {
- REALM_ASSERT(error);
- [self _handleFailedRequest:error];
- }
- return NO;
- }
- - (void)_timerFired {
- RLMServerToken refreshToken = nil;
- if (auto user = _user.lock()) {
- refreshToken = @(user->refresh_token().c_str());
- }
- if (!refreshToken) {
- unregisterRefreshHandle(_user, _path);
- return;
- }
- NSDictionary *json = @{
- kRLMSyncProviderKey: @"realm",
- kRLMSyncPathKey: @(_path.c_str()),
- kRLMSyncDataKey: refreshToken,
- kRLMSyncAppIDKey: [RLMSyncManager sharedManager].appID,
- };
- __weak RLMSyncSessionRefreshHandle *weakSelf = self;
- RLMSyncCompletionBlock handler = ^(NSError *error, NSDictionary *json) {
- [weakSelf _onRefreshCompletionWithError:error json:json];
- };
- [RLMSyncAuthEndpoint sendRequestToServer:self.authServerURL
- JSON:json
- timeout:60.0
- completion:handler];
- }
- @end
|