133 lines
4.3 KiB
Objective-C
133 lines
4.3 KiB
Objective-C
/*
|
|
* Copyright 2020 Google LLC
|
|
*
|
|
* 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 "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsBackoffController.h"
|
|
|
|
static const NSTimeInterval k24Hours = 24 * 60 * 60;
|
|
static const NSTimeInterval k30Minutes = 30 * 60;
|
|
|
|
/** The class represents `FIRInstallationsBackoffController` sate required to calculate next allowed
|
|
request time. The properties of the class are intentionally immutable because changing them
|
|
separately leads to an inconsistent state. */
|
|
@interface FIRInstallationsBackoffEventData : NSObject
|
|
|
|
@property(nonatomic, readonly) FIRInstallationsBackoffEvent eventType;
|
|
@property(nonatomic, readonly) NSDate *lastEventDate;
|
|
@property(nonatomic, readonly) NSInteger eventCount;
|
|
|
|
@property(nonatomic, readonly) NSTimeInterval backoffTimeInterval;
|
|
|
|
@end
|
|
|
|
@implementation FIRInstallationsBackoffEventData
|
|
|
|
- (instancetype)initWithEvent:(FIRInstallationsBackoffEvent)eventType
|
|
lastEventDate:(NSDate *)lastEventDate
|
|
eventCount:(NSInteger)eventCount {
|
|
self = [super init];
|
|
if (self) {
|
|
_eventType = eventType;
|
|
_lastEventDate = lastEventDate;
|
|
_eventCount = eventCount;
|
|
|
|
_backoffTimeInterval = [[self class] backoffTimeIntervalWithEvent:eventType
|
|
eventCount:eventCount];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
+ (NSTimeInterval)backoffTimeIntervalWithEvent:(FIRInstallationsBackoffEvent)eventType
|
|
eventCount:(NSInteger)eventCount {
|
|
switch (eventType) {
|
|
case FIRInstallationsBackoffEventSuccess:
|
|
return 0;
|
|
break;
|
|
|
|
case FIRInstallationsBackoffEventRecoverableFailure:
|
|
return [self recoverableErrorBackoffTimeForAttemptNumber:eventCount];
|
|
break;
|
|
|
|
case FIRInstallationsBackoffEventUnrecoverableFailure:
|
|
return k24Hours;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ (NSTimeInterval)recoverableErrorBackoffTimeForAttemptNumber:(NSInteger)attemptNumber {
|
|
NSTimeInterval exponentialInterval = pow(2, attemptNumber) + [self randomMilliseconds];
|
|
return MIN(exponentialInterval, k30Minutes);
|
|
}
|
|
|
|
+ (NSTimeInterval)randomMilliseconds {
|
|
int32_t random_millis = ABS(arc4random() % 1000);
|
|
return (double)random_millis * 0.001;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface FIRInstallationsBackoffController ()
|
|
|
|
@property(nonatomic, readonly) FIRCurrentDateProvider currentDateProvider;
|
|
|
|
@property(nonatomic, nullable) FIRInstallationsBackoffEventData *lastEventData;
|
|
|
|
@end
|
|
|
|
@implementation FIRInstallationsBackoffController
|
|
|
|
- (instancetype)init {
|
|
return [self initWithCurrentDateProvider:FIRRealCurrentDateProvider()];
|
|
}
|
|
|
|
- (instancetype)initWithCurrentDateProvider:(FIRCurrentDateProvider)currentDateProvider {
|
|
self = [super init];
|
|
if (self) {
|
|
_currentDateProvider = [currentDateProvider copy];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)isNextRequestAllowed {
|
|
@synchronized(self) {
|
|
if (self.lastEventData == nil) {
|
|
return YES;
|
|
}
|
|
|
|
NSTimeInterval timeSinceLastEvent =
|
|
[self.currentDateProvider() timeIntervalSinceDate:self.lastEventData.lastEventDate];
|
|
return timeSinceLastEvent >= self.lastEventData.backoffTimeInterval;
|
|
}
|
|
}
|
|
|
|
- (void)registerEvent:(FIRInstallationsBackoffEvent)event {
|
|
@synchronized(self) {
|
|
// Event of the same type as was registered before.
|
|
if (self.lastEventData && self.lastEventData.eventType == event) {
|
|
self.lastEventData = [[FIRInstallationsBackoffEventData alloc]
|
|
initWithEvent:event
|
|
lastEventDate:self.currentDateProvider()
|
|
eventCount:self.lastEventData.eventCount + 1];
|
|
} else { // A different event.
|
|
self.lastEventData =
|
|
[[FIRInstallationsBackoffEventData alloc] initWithEvent:event
|
|
lastEventDate:self.currentDateProvider()
|
|
eventCount:1];
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|