feat: sync httpdns sdk/platform updates without large binaries
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 <Foundation/Foundation.h>
|
||||
|
||||
@interface HttpdnsScheduleCenter : NSObject
|
||||
|
||||
/// 针对多账号场景的调度中心构造方<E980A0><E696B9>?
|
||||
/// 注意:若无需多账号隔离,可继续使<E7BBAD><E4BDBF>?sharedInstance
|
||||
- (instancetype)initWithAccountId:(NSInteger)accountId;
|
||||
|
||||
- (void)initRegion:(NSString *)region;
|
||||
|
||||
- (void)resetRegion:(NSString *)region;
|
||||
|
||||
- (void)asyncUpdateRegionScheduleConfig;
|
||||
|
||||
- (void)rotateServiceServerHost;
|
||||
|
||||
- (NSString *)currentActiveServiceServerV4Host;
|
||||
|
||||
- (NSString *)currentActiveServiceServerV6Host;
|
||||
|
||||
|
||||
#pragma mark - Expose to Testcases
|
||||
|
||||
- (void)asyncUpdateRegionScheduleConfigAtRetry:(int)retryCount;
|
||||
|
||||
- (NSString *)getActiveUpdateServerHost;
|
||||
|
||||
- (NSArray<NSString *> *)currentUpdateServerV4HostList;
|
||||
|
||||
- (NSArray<NSString *> *)currentServiceServerV4HostList;
|
||||
|
||||
- (NSArray<NSString *> *)currentUpdateServerV6HostList;
|
||||
|
||||
- (NSArray<NSString *> *)currentServiceServerV6HostList;
|
||||
|
||||
- (int)currentActiveUpdateServerHostIndex;
|
||||
|
||||
- (int)currentActiveServiceServerHostIndex;
|
||||
|
||||
@end
|
||||
405
HttpDNSSDK/sdk/ios/NewHttpDNS/Scheduler/HttpdnsScheduleCenter.m
Normal file
405
HttpDNSSDK/sdk/ios/NewHttpDNS/Scheduler/HttpdnsScheduleCenter.m
Normal 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 ()
|
||||
|
||||
// 为了简单,无论v4还是v6,都只共同维<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/NSString,屏蔽NSNull等异常输<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];
|
||||
|
||||
// 调度server列表总是服务server列表加上兜底域名
|
||||
self->_ipv4UpdateServerHostList = [HttpdnsUtil joinArrays:v4Result
|
||||
withArray:[regionConfigLoader getUpdateV4FallbackHostList:self->_currentRegion]];
|
||||
}
|
||||
|
||||
if ([HttpdnsUtil isNotEmptyArray:v6Result]) {
|
||||
self->_ipv6ServiceServerHostList = [v6Result copy];
|
||||
|
||||
// 调度server列表总是服务server列表加上兜底域名
|
||||
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
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// HttpdnsScheduleExecutor.h
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/4/11.
|
||||
// Copyright © 2017<31><37>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface HttpdnsScheduleExecutor : NSObject
|
||||
|
||||
- (NSDictionary *)fetchRegionConfigFromServer:(NSString *)updateHost error:(NSError **)pError;
|
||||
|
||||
// 多账号隔离:允许携带账号与超时初始化,避免依赖全局单例
|
||||
- (instancetype)initWithAccountId:(NSInteger)accountId timeout:(NSTimeInterval)timeoutInterval;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// HttpdnsScheduleExecutor.m
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/4/11.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsScheduleExecutor.h"
|
||||
#import "HttpdnsLog_Internal.h"
|
||||
#import "HttpdnsService_Internal.h"
|
||||
#import "HttpdnsInternalConstant.h"
|
||||
#import "HttpdnsUtil.h"
|
||||
#import "HttpdnsScheduleCenter.h"
|
||||
#import "HttpdnsHostObject.h"
|
||||
#import "HttpdnsReachability.h"
|
||||
#import "HttpdnsPublicConstant.h"
|
||||
#import "HttpdnsNWHTTPClient.h"
|
||||
|
||||
@interface HttpdnsScheduleExecutor ()
|
||||
@property (nonatomic, strong) HttpdnsNWHTTPClient *httpClient;
|
||||
@end
|
||||
|
||||
@implementation HttpdnsScheduleExecutor {
|
||||
NSInteger _accountId;
|
||||
NSTimeInterval _timeoutInterval;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
// 兼容旧路径:使用全局单例读取,但多账号场景下建议使用新init接口
|
||||
_accountId = [HttpDnsService sharedInstance].accountID;
|
||||
_timeoutInterval = [HttpDnsService sharedInstance].timeoutInterval;
|
||||
_httpClient = [HttpdnsNWHTTPClient sharedInstance];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAccountId:(NSInteger)accountId timeout:(NSTimeInterval)timeoutInterval {
|
||||
if (!(self = [self init])) {
|
||||
return nil;
|
||||
}
|
||||
_accountId = accountId;
|
||||
_timeoutInterval = timeoutInterval;
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接 URL
|
||||
* 2024.6.12今天起,调度服务由后端就近调度,不再需要传入region参数,但为了兼容不传region默认就是国内region的逻辑,默认都传入region=global
|
||||
* https://203.107.1.1/100000/ss?region=global&platform=ios&sdk_version=3.1.7&sid=LpmJIA2CUoi4&net=wifi
|
||||
*/
|
||||
- (NSString *)constructRequestURLWithUpdateHost:(NSString *)updateHost {
|
||||
NSString *urlPath = [NSString stringWithFormat:@"%ld/ss?region=global&platform=ios&sdk_version=%@", (long)_accountId, HTTPDNS_IOS_SDK_VERSION];
|
||||
urlPath = [self urlFormatSidNetBssid:urlPath];
|
||||
urlPath = [urlPath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "].invertedSet];
|
||||
return [NSString stringWithFormat:@"https://%@/%@", updateHost, urlPath];
|
||||
}
|
||||
|
||||
// url 添加 sid net
|
||||
- (NSString *)urlFormatSidNetBssid:(NSString *)url {
|
||||
NSString *sessionId = [HttpdnsUtil generateSessionID];
|
||||
if ([HttpdnsUtil isNotEmptyString:sessionId]) {
|
||||
url = [NSString stringWithFormat:@"%@&sid=%@", url, sessionId];
|
||||
}
|
||||
|
||||
NSString *netType = [[HttpdnsReachability sharedInstance] currentReachabilityString];
|
||||
if ([HttpdnsUtil isNotEmptyString:netType]) {
|
||||
url = [NSString stringWithFormat:@"%@&net=%@", url, netType];
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
- (NSDictionary *)fetchRegionConfigFromServer:(NSString *)updateHost error:(NSError **)pError {
|
||||
NSString *fullUrlStr = [self constructRequestURLWithUpdateHost:updateHost];
|
||||
HttpdnsLogDebug("ScRequest URL: %@", fullUrlStr);
|
||||
NSTimeInterval timeout = _timeoutInterval > 0 ? _timeoutInterval : HTTPDNS_DEFAULT_REQUEST_TIMEOUT_INTERVAL;
|
||||
NSString *userAgent = [HttpdnsUtil generateUserAgent];
|
||||
|
||||
NSError *requestError = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.httpClient performRequestWithURLString:fullUrlStr
|
||||
userAgent:userAgent
|
||||
timeout:timeout
|
||||
error:&requestError];
|
||||
if (!response) {
|
||||
if (pError) {
|
||||
*pError = requestError;
|
||||
HttpdnsLogDebug("ScRequest failed with url: %@, error: %@", fullUrlStr, requestError);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
NSDictionary *dict = @{@"ResponseCode": [NSString stringWithFormat:@"%ld", (long)response.statusCode]};
|
||||
if (pError) {
|
||||
*pError = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_NO_DATA_ERROR_CODE
|
||||
userInfo:dict];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSError *jsonError = nil;
|
||||
id jsonValue = [NSJSONSerialization JSONObjectWithData:response.body options:kNilOptions error:&jsonError];
|
||||
if (jsonError) {
|
||||
if (pError) {
|
||||
*pError = jsonError;
|
||||
HttpdnsLogDebug("ScRequest JSON parse error, url: %@, error: %@", fullUrlStr, jsonError);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary *result = [HttpdnsUtil getValidDictionaryFromJson:jsonValue];
|
||||
if (result) {
|
||||
HttpdnsLogDebug("ScRequest get response: %@", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (pError) {
|
||||
*pError = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to parse JSON response"}];
|
||||
}
|
||||
if (pError != NULL) {
|
||||
HttpdnsLogDebug("ScRequest failed with url: %@, response body invalid", fullUrlStr);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user