feat: sync httpdns sdk/platform updates without large binaries

This commit is contained in:
robin
2026-03-04 17:59:14 +08:00
parent 853897a6f8
commit 532891fad0
700 changed files with 6096 additions and 2712 deletions

View File

@@ -0,0 +1,405 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 "HttpdnsScheduleCenter.h"
#import "HttpdnsPersistenceUtils.h"
#import "HttpdnsLog_Internal.h"
#import "HttpdnsInternalConstant.h"
#import "HttpdnsRequestManager.h"
#import "HttpdnsService_Internal.h"
#import "HttpdnsScheduleExecutor.h"
#import "HttpdnsRemoteResolver.h"
#import "HttpdnsUtil.h"
#import "HttpdnsPublicConstant.h"
#import "HttpdnsRegionConfigLoader.h"
#import "HttpdnsIpStackDetector.h"
static NSString *const kLastUpdateUnixTimestampKey = @"last_update_unix_timestamp";
static NSString *const kScheduleRegionConfigLocalCacheFileName = @"schedule_center_result";
static int const MAX_UPDATE_RETRY_COUNT = 2;
@interface HttpdnsScheduleCenter ()
// v4v6<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?
// <EFBFBD><EFBFBD>?
@property (nonatomic, assign) int currentActiveServiceHostIndex;
@property (nonatomic, assign) int currentActiveUpdateHostIndex;
@property (nonatomic, copy) NSArray<NSString *> *ipv4ServiceServerHostList;
@property (nonatomic, copy) NSArray<NSString *> *ipv6ServiceServerHostList;
@property (nonatomic, copy) NSArray<NSString *> *ipv4UpdateServerHostList;
@property (nonatomic, copy) NSArray<NSString *> *ipv6UpdateServerHostList;
@property (nonatomic, strong) dispatch_queue_t scheduleFetchConfigAsyncQueue;
@property (nonatomic, strong) dispatch_queue_t scheduleConfigLocalOperationQueue;
@property (nonatomic, copy) NSString *scheduleCenterResultPath;
@property (nonatomic, copy) NSDate *lastScheduleCenterConnectDate;
@property (nonatomic, copy) NSString *currentRegion;
@property (nonatomic, assign) NSInteger accountId;
@end
@implementation HttpdnsScheduleCenter
- (instancetype)initWithAccountId:(NSInteger)accountId {
if (self = [self init]) {
_accountId = accountId;
// ID
NSString *dir = [HttpdnsPersistenceUtils scheduleCenterResultDirectoryForAccount:accountId];
_scheduleCenterResultPath = [dir stringByAppendingPathComponent:kScheduleRegionConfigLocalCacheFileName];
}
return self;
}
- (instancetype)init {
if (self = [super init]) {
_scheduleFetchConfigAsyncQueue = dispatch_queue_create("com.new.httpdns.scheduleFetchConfigAsyncQueue", DISPATCH_QUEUE_CONCURRENT);
_scheduleConfigLocalOperationQueue = dispatch_queue_create("com.new.httpdns.scheduleConfigLocalOperationQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
self->_currentActiveUpdateHostIndex = 0;
self->_currentActiveServiceHostIndex = 0;
});
_scheduleCenterResultPath = [[HttpdnsPersistenceUtils scheduleCenterResultDirectory]
stringByAppendingPathComponent:kScheduleRegionConfigLocalCacheFileName];
// <EFBFBD><EFBFBD>?
_lastScheduleCenterConnectDate = [NSDate dateWithTimeIntervalSinceNow:(- 24 * 60 * 60)];
}
return self;
}
- (void)initRegion:(NSString *)region {
if (![[HttpdnsRegionConfigLoader getAvailableRegionList] containsObject:region]) {
region = NEW_HTTPDNS_DEFAULT_REGION_KEY;
}
// region<EFBFBD><EFBFBD>?
// region<EFBFBD><EFBFBD>?
[self initServerListByRegion:region];
//
[self loadRegionConfigFromLocalCache];
}
- (void)resetRegion:(NSString *)region {
[self initServerListByRegion:region];
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
self.currentActiveServiceHostIndex = 0;
self.currentActiveUpdateHostIndex = 0;
});
// region<EFBFBD><EFBFBD>?
[self asyncUpdateRegionScheduleConfig];
}
- (void)loadRegionConfigFromLocalCache {
dispatch_async(self.scheduleFetchConfigAsyncQueue, ^{
id obj = [HttpdnsPersistenceUtils getJSONFromPath:self.scheduleCenterResultPath];
if (![obj isKindOfClass:[NSDictionary class]]) {
//
return;
}
NSDictionary *scheduleCenterResult = (NSDictionary *)obj;
// NSNumber/NSStringNSNull<EFBFBD><EFBFBD>?
id ts = [scheduleCenterResult objectForKey:kLastUpdateUnixTimestampKey];
if ([ts respondsToSelector:@selector(doubleValue)]) {
NSDate *lastUpdateDate = [NSDate dateWithTimeIntervalSince1970:[ts doubleValue]];
self->_lastScheduleCenterConnectDate = lastUpdateDate;
}
[self updateRegionConfig:scheduleCenterResult];
});
}
// <EFBFBD><EFBFBD>?
- (void)asyncUpdateRegionConfigAfterAtLeast:(NSTimeInterval)interval {
__block BOOL shouldUpdate = NO;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
NSDate *now = [NSDate date];
if ([now timeIntervalSinceDate:self->_lastScheduleCenterConnectDate] > interval) {
self->_lastScheduleCenterConnectDate = now;
shouldUpdate = YES;
}
});
if (shouldUpdate) {
[self asyncUpdateRegionScheduleConfig];
}
}
- (void)asyncUpdateRegionScheduleConfig {
[self asyncUpdateRegionScheduleConfigAtRetry:0];
}
- (void)asyncUpdateRegionScheduleConfigAtRetry:(int)retryCount {
if (retryCount > MAX_UPDATE_RETRY_COUNT) {
return;
}
dispatch_async(_scheduleFetchConfigAsyncQueue, ^(void) {
NSTimeInterval timeout = [HttpDnsService getInstanceByAccountId:self.accountId].timeoutInterval;
HttpdnsScheduleExecutor *scheduleCenterExecutor = [[HttpdnsScheduleExecutor alloc] initWithAccountId:self.accountId timeout:timeout];
NSError *error = nil;
NSString *updateHost = [self getActiveUpdateServerHost];
NSDictionary *scheduleCenterResult = [scheduleCenterExecutor fetchRegionConfigFromServer:updateHost error:&error];
if (error || !scheduleCenterResult) {
HttpdnsLogDebug("Update region config failed, error: %@", error);
// <EFBFBD><EFBFBD>?
[self rotateUpdateServerHost];
// 3<EFBFBD><EFBFBD>?
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((retryCount + 1) * NSEC_PER_SEC)), self->_scheduleFetchConfigAsyncQueue, ^{
[self asyncUpdateRegionScheduleConfigAtRetry:retryCount + 1];
});
return;
}
NSMutableDictionary *toSave = [scheduleCenterResult mutableCopy];
toSave[kLastUpdateUnixTimestampKey] = @([[NSDate date] timeIntervalSince1970]);
BOOL saveSuccess = [HttpdnsPersistenceUtils saveJSON:toSave toPath:self.scheduleCenterResultPath];
HttpdnsLogDebug("Save region config to local cache %@", saveSuccess ? @"successfully" : @"failed");
[self updateRegionConfig:scheduleCenterResult];
});
}
- (void)updateRegionConfig:(NSDictionary *)scheduleCenterResult {
NSArray *v4Result = [scheduleCenterResult objectForKey:kNewHttpdnsRegionConfigV4HostKey];
NSArray *v6Result = [scheduleCenterResult objectForKey:kNewHttpdnsRegionConfigV6HostKey];
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
HttpdnsRegionConfigLoader *regionConfigLoader = [HttpdnsRegionConfigLoader sharedInstance];
if ([HttpdnsUtil isNotEmptyArray:v4Result]) {
self->_ipv4ServiceServerHostList = [v4Result copy];
// serverserver
self->_ipv4UpdateServerHostList = [HttpdnsUtil joinArrays:v4Result
withArray:[regionConfigLoader getUpdateV4FallbackHostList:self->_currentRegion]];
}
if ([HttpdnsUtil isNotEmptyArray:v6Result]) {
self->_ipv6ServiceServerHostList = [v6Result copy];
// serverserver
self->_ipv6UpdateServerHostList = [HttpdnsUtil joinArrays:v6Result
withArray:[regionConfigLoader getUpdateV6FallbackHostList:self->_currentRegion]];
}
self->_currentActiveUpdateHostIndex = 0;
self->_currentActiveServiceHostIndex = 0;
});
}
- (NSString *)getActiveUpdateServerHost {
HttpdnsIPStackType currentStack = [[HttpdnsIpStackDetector sharedInstance] currentIpStack];
if (currentStack == kHttpdnsIpv6Only) {
NSString *v6Host = [self currentActiveUpdateServerV6Host];
if ([HttpdnsUtil isIPv6Address:v6Host]) {
return [NSString stringWithFormat:@"[%@]", v6Host];
}
return v6Host;
}
return [self currentActiveUpdateServerV4Host];
}
- (void)initServerListByRegion:(NSString *)region {
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
self->_currentRegion = region;
HttpdnsRegionConfigLoader *regionConfigLoader = [HttpdnsRegionConfigLoader sharedInstance];
self.ipv4ServiceServerHostList = [regionConfigLoader getSeriveV4HostList:region];
self.ipv4UpdateServerHostList = [HttpdnsUtil joinArrays:[regionConfigLoader getSeriveV4HostList:region]
withArray:[regionConfigLoader getUpdateV4FallbackHostList:region]];
self.ipv6ServiceServerHostList = [regionConfigLoader getSeriveV6HostList:region];
self.ipv6UpdateServerHostList = [HttpdnsUtil joinArrays:[regionConfigLoader getSeriveV6HostList:region]
withArray:[regionConfigLoader getUpdateV6FallbackHostList:region]];
});
}
- (void)rotateServiceServerHost {
__block int timeToUpdate = NO;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
self.currentActiveServiceHostIndex++;
int total = (int)self.ipv4ServiceServerHostList.count + (int)self.ipv6ServiceServerHostList.count;
if (self.currentActiveServiceHostIndex % total == 0) {
timeToUpdate = YES;
}
});
if (timeToUpdate) {
// server<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?0
[self asyncUpdateRegionConfigAfterAtLeast:30];
}
}
- (void)rotateUpdateServerHost {
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
self.currentActiveUpdateHostIndex++;
});
}
- (NSString *)currentActiveUpdateServerV4Host {
__block NSString *host = nil;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
int count = (int)self.ipv4UpdateServerHostList.count;
if (count == 0) {
HttpdnsLogDebug("Severe error: update v4 ip list is empty, it should never happen");
return;
}
int index = self.currentActiveUpdateHostIndex % count;
host = self.ipv4UpdateServerHostList[index];
});
return host;
}
- (NSString *)currentActiveServiceServerV4Host {
// <EFBFBD><EFBFBD>?
// httpdns<EFBFBD><EFBFBD>?
[self asyncUpdateRegionConfigAfterAtLeast:(24 * 60 * 60)];
// HTTPDNS_DEBUG_V4_SERVICE_IP
NSString *debugV4ServiceIP = [[[NSProcessInfo processInfo] environment] objectForKey:@"HTTPDNS_DEBUG_V4_SERVICE_IP"];
if ([HttpdnsUtil isNotEmptyString:debugV4ServiceIP]) {
HttpdnsLogDebug("Using debug v4 service IP from environment: %@", debugV4ServiceIP);
return debugV4ServiceIP;
}
__block NSString *host = nil;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
int count = (int)self.ipv4ServiceServerHostList.count;
if (count == 0) {
HttpdnsLogDebug("Severe error: service v4 ip list is empty, it should never happen");
return;
}
int index = self.currentActiveServiceHostIndex % count;
host = self.ipv4ServiceServerHostList[index];
});
return host;
}
- (NSString *)currentActiveUpdateServerV6Host {
__block NSString *host = nil;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
int count = (int)self.ipv6UpdateServerHostList.count;
if (count == 0) {
HttpdnsLogDebug("Severe error: update v6 ip list is empty, it should never happen");
return;
}
int index = self.currentActiveUpdateHostIndex % count;
host = self.ipv6UpdateServerHostList[index];
});
return host;
}
- (NSString *)currentActiveServiceServerV6Host {
//
[self asyncUpdateRegionConfigAfterAtLeast:(24 * 60 * 60)];
// HTTPDNS_DEBUG_V6_SERVICE_IP
NSString *debugV6ServiceIP = [[[NSProcessInfo processInfo] environment] objectForKey:@"HTTPDNS_DEBUG_V6_SERVICE_IP"];
if ([HttpdnsUtil isNotEmptyString:debugV6ServiceIP]) {
HttpdnsLogDebug("Using debug v6 service IP from environment: %@", debugV6ServiceIP);
return debugV6ServiceIP;
}
__block NSString *host = nil;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
int count = (int)self.ipv6ServiceServerHostList.count;
if (count == 0) {
HttpdnsLogDebug("Severe error: service v6 ip list is empty, it should never happen");
return;
}
int index = self.currentActiveServiceHostIndex % count;
host = self.ipv6ServiceServerHostList[index];
});
if ([HttpdnsUtil isIPv6Address:host]) {
host = [NSString stringWithFormat:@"[%@]", host];
}
return host;
}
#pragma mark - For Test Only
- (NSArray<NSString *> *)currentUpdateServerV4HostList {
__block NSArray<NSString *> *list = nil;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
list = [self.ipv4UpdateServerHostList copy];
});
return list;
}
- (NSArray<NSString *> *)currentServiceServerV4HostList {
__block NSArray<NSString *> *list = nil;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
list = [self.ipv4ServiceServerHostList copy];
});
return list;
}
- (NSArray<NSString *> *)currentUpdateServerV6HostList {
__block NSArray<NSString *> *list = nil;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
list = [self.ipv6UpdateServerHostList copy];
});
return list;
}
- (NSArray<NSString *> *)currentServiceServerV6HostList {
__block NSArray<NSString *> *list = nil;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
list = [self.ipv6ServiceServerHostList copy];
});
return list;
}
- (int)currentActiveUpdateServerHostIndex {
__block int index = 0;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
index = self.currentActiveUpdateHostIndex;
});
return index;
}
- (int)currentActiveServiceServerHostIndex {
__block int index = 0;
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
index = self.currentActiveServiceHostIndex;
});
return index;
}
@end