带阿里标识的版本
This commit is contained in:
@@ -19,12 +19,12 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <AlicloudHTTPDNS/HttpdnsLog.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsPublicConstant.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsService.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsRequest.h>
|
||||
#import <AlicloudHTTPDNS/HttpDnsResult.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsEdgeService.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsLoggerProtocol.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsDegradationDelegate.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsIpStackDetector.h>
|
||||
#import <TrustHTTPDNS/HttpdnsLog.h>
|
||||
#import <TrustHTTPDNS/HttpdnsPublicConstant.h>
|
||||
#import <TrustHTTPDNS/HttpdnsService.h>
|
||||
#import <TrustHTTPDNS/HttpdnsRequest.h>
|
||||
#import <TrustHTTPDNS/HttpDnsResult.h>
|
||||
#import <TrustHTTPDNS/HttpdnsEdgeService.h>
|
||||
#import <TrustHTTPDNS/HttpdnsLoggerProtocol.h>
|
||||
#import <TrustHTTPDNS/HttpdnsDegradationDelegate.h>
|
||||
#import <TrustHTTPDNS/HttpdnsIpStackDetector.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsInternalConstant.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2025/03/10.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef HTTPDNS_INTERNAL_CONSTANT_H
|
||||
@@ -19,42 +19,42 @@ static const int HTTPDNS_DEFAULT_REQUEST_TIMEOUT_INTERVAL = 3;
|
||||
|
||||
static const NSUInteger HTTPDNS_DEFAULT_AUTH_TIMEOUT_INTERVAL = 10 * 60;
|
||||
|
||||
static NSString *const ALICLOUD_HTTPDNS_VALID_SERVER_CERTIFICATE_IP = @"203.107.1.1";
|
||||
static NSString *const Trust_HTTPDNS_VALID_SERVER_CERTIFICATE_IP = @"203.107.1.1";
|
||||
|
||||
// 在iOS14和iOS16,网络信息的获取权限受到越来越紧的限制
|
||||
// 在iOS14和iOS16,网络信息的获取权限受到越来越紧的限<EFBFBD><EFBFBD>?
|
||||
// 除非用户主动声明需要相关entitlement,不然只能拿到空信息
|
||||
// 考虑大多数用户并不会申请这些权限,我们放弃针对细节的网络信息做缓存粒度隔离
|
||||
// 出于兼容性考虑,网络运营商只有default一种类型
|
||||
// 考虑大多数用户并不会申请这些权限,我们放弃针对细节的网络信息做缓存粒度隔<EFBFBD><EFBFBD>?
|
||||
// 出于兼容性考虑,网络运营商只有default一种类<EFBFBD><EFBFBD>?
|
||||
#define HTTPDNS_DEFAULT_NETWORK_CARRIER_NAME @"default"
|
||||
|
||||
// 调度地址示例:http://106.11.90.200/sc/httpdns_config?account_id=153519&platform=ios&sdk_version=1.6.1
|
||||
static NSString *const ALICLOUD_HTTPDNS_SCHEDULE_CENTER_REQUEST_HOST = @"httpdns-sc.aliyuncs.com";
|
||||
static NSString *const Trust_HTTPDNS_SCHEDULE_CENTER_REQUEST_HOST = @"httpdns-sc.TrustAPPcs.com";
|
||||
|
||||
static NSString *const ALICLOUD_HTTPDNS_ERROR_MESSAGE_KEY = @"ErrorMessage";
|
||||
static NSString *const Trust_HTTPDNS_ERROR_MESSAGE_KEY = @"ErrorMessage";
|
||||
|
||||
static NSString *const kAlicloudHttpdnsRegionConfigV4HostKey = @"service_ip";
|
||||
static NSString *const kAlicloudHttpdnsRegionConfigV6HostKey = @"service_ipv6";
|
||||
static NSString *const kTrustHttpdnsRegionConfigV4HostKey = @"service_ip";
|
||||
static NSString *const kTrustHttpdnsRegionConfigV6HostKey = @"service_ipv6";
|
||||
|
||||
static NSString *const kAlicloudHttpdnsRegionKey = @"HttpdnsRegion";
|
||||
static NSString *const kTrustHttpdnsRegionKey = @"HttpdnsRegion";
|
||||
|
||||
#define SECONDS_OF_ONE_YEAR 365 * 24 * 60 * 60
|
||||
|
||||
static NSString *const ALICLOUD_HTTPDNS_ERROR_DOMAIN = @"HttpdnsErrorDomain";
|
||||
static NSString *const Trust_HTTPDNS_ERROR_DOMAIN = @"HttpdnsErrorDomain";
|
||||
|
||||
static NSInteger const ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE = 10003;
|
||||
static NSInteger const ALICLOUD_HTTPDNS_HTTP_COMMON_ERROR_CODE = 10004;
|
||||
static NSInteger const Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE = 10003;
|
||||
static NSInteger const Trust_HTTPDNS_HTTP_COMMON_ERROR_CODE = 10004;
|
||||
|
||||
static NSInteger const ALICLOUD_HTTPDNS_HTTPS_TIMEOUT_ERROR_CODE = 10005;
|
||||
static NSInteger const ALICLOUD_HTTPDNS_HTTP_TIMEOUT_ERROR_CODE = 10006;
|
||||
static NSInteger const ALICLOUD_HTTPDNS_HTTP_OPEN_STREAM_ERROR_CODE = 10007;
|
||||
static NSInteger const ALICLOUD_HTTPDNS_HTTPS_NO_DATA_ERROR_CODE = 10008;
|
||||
static NSInteger const Trust_HTTPDNS_HTTPS_TIMEOUT_ERROR_CODE = 10005;
|
||||
static NSInteger const Trust_HTTPDNS_HTTP_TIMEOUT_ERROR_CODE = 10006;
|
||||
static NSInteger const Trust_HTTPDNS_HTTP_OPEN_STREAM_ERROR_CODE = 10007;
|
||||
static NSInteger const Trust_HTTPDNS_HTTPS_NO_DATA_ERROR_CODE = 10008;
|
||||
|
||||
static NSInteger const ALICLOUD_HTTP_UNSUPPORTED_STATUS_CODE = 10013;
|
||||
static NSInteger const ALICLOUD_HTTP_PARSE_JSON_FAILED = 10014;
|
||||
static NSInteger const Trust_HTTP_UNSUPPORTED_STATUS_CODE = 10013;
|
||||
static NSInteger const Trust_HTTP_PARSE_JSON_FAILED = 10014;
|
||||
|
||||
// 加密错误码
|
||||
static NSInteger const ALICLOUD_HTTPDNS_ENCRYPT_INVALID_PARAMS_ERROR_CODE = 10021;
|
||||
static NSInteger const ALICLOUD_HTTPDNS_ENCRYPT_RANDOM_IV_ERROR_CODE = 10022;
|
||||
static NSInteger const ALICLOUD_HTTPDNS_ENCRYPT_FAILED_ERROR_CODE = 10023;
|
||||
// 加密错误<EFBFBD><EFBFBD>?
|
||||
static NSInteger const Trust_HTTPDNS_ENCRYPT_INVALID_PARAMS_ERROR_CODE = 10021;
|
||||
static NSInteger const Trust_HTTPDNS_ENCRYPT_RANDOM_IV_ERROR_CODE = 10022;
|
||||
static NSInteger const Trust_HTTPDNS_ENCRYPT_FAILED_ERROR_CODE = 10023;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsPublicConstant.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/6/16.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef PublicConstant_h
|
||||
@@ -11,11 +11,11 @@
|
||||
|
||||
static NSString *const HTTPDNS_IOS_SDK_VERSION = @"1.0.0";
|
||||
|
||||
#define ALICLOUD_HTTPDNS_DEFAULT_REGION_KEY @"cn"
|
||||
#define ALICLOUD_HTTPDNS_HONGKONG_REGION_KEY @"hk"
|
||||
#define ALICLOUD_HTTPDNS_SINGAPORE_REGION_KEY @"sg"
|
||||
#define ALICLOUD_HTTPDNS_GERMANY_REGION_KEY @"de"
|
||||
#define ALICLOUD_HTTPDNS_AMERICA_REGION_KEY @"us"
|
||||
#define Trust_HTTPDNS_DEFAULT_REGION_KEY @"cn"
|
||||
#define Trust_HTTPDNS_HONGKONG_REGION_KEY @"hk"
|
||||
#define Trust_HTTPDNS_SINGAPORE_REGION_KEY @"sg"
|
||||
#define Trust_HTTPDNS_GERMANY_REGION_KEY @"de"
|
||||
#define Trust_HTTPDNS_AMERICA_REGION_KEY @"us"
|
||||
|
||||
|
||||
#endif /* PublicConstant_h */
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsRegionConfigLoader.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/6/16.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
//
|
||||
// HttpdnsRegionConfigLoader.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/6/16.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsRegionConfigLoader.h"
|
||||
#import "HttpdnsPublicConstant.h"
|
||||
|
||||
static NSString *const kServiceV4Key = @"ALICLOUD_HTTPDNS_SERVICE_HOST_V4_KEY";
|
||||
static NSString *const kUpdateV4FallbackHostKey = @"ALICLOUD_HTTPDNS_UPDATE_HOST_V4_KEY";
|
||||
static NSString *const kServiceV6Key = @"ALICLOUD_HTTPDNS_SERVICE_HOST_V6_KEY";
|
||||
static NSString *const kUpdateV6FallbackHostKey = @"ALICLOUD_HTTPDNS_UPDATE_HOST_V6_KEY";
|
||||
static NSString *const kServiceV4Key = @"Trust_HTTPDNS_SERVICE_HOST_V4_KEY";
|
||||
static NSString *const kUpdateV4FallbackHostKey = @"Trust_HTTPDNS_UPDATE_HOST_V4_KEY";
|
||||
static NSString *const kServiceV6Key = @"Trust_HTTPDNS_SERVICE_HOST_V6_KEY";
|
||||
static NSString *const kUpdateV6FallbackHostKey = @"Trust_HTTPDNS_UPDATE_HOST_V6_KEY";
|
||||
|
||||
static NSArray<NSString *> *ALICLOUD_HTTPDNS_AVAILABLE_REGION_LIST = nil;
|
||||
static NSArray<NSString *> *Trust_HTTPDNS_AVAILABLE_REGION_LIST = nil;
|
||||
|
||||
@interface HttpdnsRegionConfigLoader ()
|
||||
|
||||
@@ -25,12 +25,12 @@ static NSArray<NSString *> *ALICLOUD_HTTPDNS_AVAILABLE_REGION_LIST = nil;
|
||||
@implementation HttpdnsRegionConfigLoader
|
||||
|
||||
+ (void)initialize {
|
||||
ALICLOUD_HTTPDNS_AVAILABLE_REGION_LIST = @[
|
||||
ALICLOUD_HTTPDNS_DEFAULT_REGION_KEY,
|
||||
ALICLOUD_HTTPDNS_HONGKONG_REGION_KEY,
|
||||
ALICLOUD_HTTPDNS_SINGAPORE_REGION_KEY,
|
||||
ALICLOUD_HTTPDNS_GERMANY_REGION_KEY,
|
||||
ALICLOUD_HTTPDNS_AMERICA_REGION_KEY
|
||||
Trust_HTTPDNS_AVAILABLE_REGION_LIST = @[
|
||||
Trust_HTTPDNS_DEFAULT_REGION_KEY,
|
||||
Trust_HTTPDNS_HONGKONG_REGION_KEY,
|
||||
Trust_HTTPDNS_SINGAPORE_REGION_KEY,
|
||||
Trust_HTTPDNS_GERMANY_REGION_KEY,
|
||||
Trust_HTTPDNS_AMERICA_REGION_KEY
|
||||
];
|
||||
}
|
||||
|
||||
@@ -51,40 +51,40 @@ static NSArray<NSString *> *ALICLOUD_HTTPDNS_AVAILABLE_REGION_LIST = nil;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)getAvailableRegionList {
|
||||
return ALICLOUD_HTTPDNS_AVAILABLE_REGION_LIST;
|
||||
return Trust_HTTPDNS_AVAILABLE_REGION_LIST;
|
||||
}
|
||||
|
||||
- (void)loadRegionConfig {
|
||||
self.regionConfig = @{
|
||||
ALICLOUD_HTTPDNS_DEFAULT_REGION_KEY: @{
|
||||
Trust_HTTPDNS_DEFAULT_REGION_KEY: @{
|
||||
kServiceV4Key: @[@"203.107.1.1", @"203.107.1.97", @"203.107.1.100", @"203.119.238.240", @"106.11.25.239", @"59.82.99.47"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-cn.httpdns.aliyuncs.com"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-cn.httpdns.TrustAPPcs.com"],
|
||||
kServiceV6Key: @[@"2401:b180:7001::31d", @"2401:b180:2000:30::1c", @"2401:b180:2000:20::10", @"2401:b180:2000:30::1c"],
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-cn.httpdns.aliyuncs.com"]
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-cn.httpdns.TrustAPPcs.com"]
|
||||
},
|
||||
ALICLOUD_HTTPDNS_HONGKONG_REGION_KEY: @{
|
||||
Trust_HTTPDNS_HONGKONG_REGION_KEY: @{
|
||||
kServiceV4Key: @[@"47.56.234.194", @"47.56.119.115"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-hk.httpdns.aliyuncs.com"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-hk.httpdns.TrustAPPcs.com"],
|
||||
kServiceV6Key: @[@"240b:4000:f10::178", @"240b:4000:f10::188"],
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-hk.httpdns.aliyuncs.com"]
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-hk.httpdns.TrustAPPcs.com"]
|
||||
},
|
||||
ALICLOUD_HTTPDNS_SINGAPORE_REGION_KEY: @{
|
||||
Trust_HTTPDNS_SINGAPORE_REGION_KEY: @{
|
||||
kServiceV4Key: @[@"161.117.200.122", @"47.74.222.190"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-sg.httpdns.aliyuncs.com"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-sg.httpdns.TrustAPPcs.com"],
|
||||
kServiceV6Key: @[@"240b:4000:f10::178", @"240b:4000:f10::188"],
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-sg.httpdns.aliyuncs.com"]
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-sg.httpdns.TrustAPPcs.com"]
|
||||
},
|
||||
ALICLOUD_HTTPDNS_GERMANY_REGION_KEY: @{
|
||||
Trust_HTTPDNS_GERMANY_REGION_KEY: @{
|
||||
kServiceV4Key: @[@"47.89.80.182", @"47.246.146.77"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-de.httpdns.aliyuncs.com"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-de.httpdns.TrustAPPcs.com"],
|
||||
kServiceV6Key: @[@"2404:2280:3000::176", @"2404:2280:3000::188"],
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-de.httpdns.aliyuncs.com"]
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-de.httpdns.TrustAPPcs.com"]
|
||||
},
|
||||
ALICLOUD_HTTPDNS_AMERICA_REGION_KEY: @{
|
||||
Trust_HTTPDNS_AMERICA_REGION_KEY: @{
|
||||
kServiceV4Key: @[@"47.246.131.175", @"47.246.131.141"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-us.httpdns.aliyuncs.com"],
|
||||
kUpdateV4FallbackHostKey: @[@"resolvers-us.httpdns.TrustAPPcs.com"],
|
||||
kServiceV6Key: @[@"2404:2280:4000::2bb", @"2404:2280:4000::23e"],
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-us.httpdns.aliyuncs.com"]
|
||||
kUpdateV6FallbackHostKey: @[@"resolvers-us.httpdns.TrustAPPcs.com"]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#import "HttpdnsEdgeService.h"
|
||||
#import "HttpdnsEdgeService.h"
|
||||
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsLocalResolver.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2025/3/16.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsLocalResolver.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2025/3/16.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsLocalResolver.h"
|
||||
@@ -34,7 +34,7 @@
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC; // 同时支持IPv4和IPv6
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP (对DNS解析来说通常不重要)
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP (对DNS解析来说通常不重<EFBFBD><EFBFBD>?
|
||||
|
||||
// 3. 执行getaddrinfo解析
|
||||
struct addrinfo *res = NULL;
|
||||
@@ -105,7 +105,7 @@
|
||||
for (NSString *ipStr in ipv4Array) {
|
||||
HttpdnsIpObject *ipObj = [[HttpdnsIpObject alloc] init];
|
||||
[ipObj setIp:ipStr]; // ipObj.ip = ipStr
|
||||
// connectedRT默认为0
|
||||
// connectedRT默认<EFBFBD><EFBFBD>?
|
||||
[v4IpObjects addObject:ipObj];
|
||||
}
|
||||
}
|
||||
@@ -123,17 +123,17 @@
|
||||
[hostObject setV4Ips:v4IpObjects];
|
||||
[hostObject setV6Ips:v6IpObjects];
|
||||
|
||||
// IPv4和IPv6的默认TTL为60秒
|
||||
// IPv4和IPv6的默认TTL<EFBFBD><EFBFBD>?0<EFBFBD><EFBFBD>?
|
||||
[hostObject setV4TTL:60];
|
||||
[hostObject setV6TTL:60];
|
||||
|
||||
// 自定义ttl
|
||||
[HttpdnsUtil processCustomTTL:hostObject forHost:host service:service];
|
||||
|
||||
// 当前时间(自1970年以来的秒数)
|
||||
// 当前时间(<EFBFBD><EFBFBD>?970年以来的秒数)
|
||||
int64_t now = (int64_t)[[NSDate date] timeIntervalSince1970];
|
||||
|
||||
// 更新最后查询时间
|
||||
// 更新最后查询时<EFBFBD><EFBFBD>?
|
||||
[hostObject setLastIPv4LookupTime:now];
|
||||
[hostObject setLastIPv6LookupTime:now];
|
||||
|
||||
@@ -141,8 +141,8 @@
|
||||
[hostObject setHasNoIpv4Record:(v4IpObjects.count == 0)];
|
||||
[hostObject setHasNoIpv6Record:(v6IpObjects.count == 0)];
|
||||
|
||||
// 如果需要,可以在这里设置clientIp或额外字段
|
||||
// 现在保留为默认值/空
|
||||
// 如果需要,可以在这里设置clientIp或额外字<EFBFBD><EFBFBD>?
|
||||
// 现在保留为默认<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?
|
||||
|
||||
return hostObject;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
+ (void)initialize {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_streamOperateSyncQueue = dispatch_queue_create("com.alibaba.sdk.httpdns.runloopOperateQueue.HttpdnsRequest", DISPATCH_QUEUE_SERIAL);
|
||||
_streamOperateSyncQueue = dispatch_queue_create("com.Trust.sdk.httpdns.runloopOperateQueue.HttpdnsRequest", DISPATCH_QUEUE_SERIAL);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 验证响应码
|
||||
// 验证响应<EFBFBD><EFBFBD>?
|
||||
if (![self validateResponseCode:json]) {
|
||||
return nil;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 获取所有答案
|
||||
// 获取所有答<EFBFBD><EFBFBD>?
|
||||
NSArray *answers = [self getAnswersFromData:data];
|
||||
if (!answers) {
|
||||
return nil;
|
||||
@@ -112,7 +112,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return hostObjects;
|
||||
}
|
||||
|
||||
// 验证响应码
|
||||
// 验证响应<EFBFBD><EFBFBD>?
|
||||
- (BOOL)validateResponseCode:(NSDictionary *)json {
|
||||
NSString *code = [json objectForKey:@"code"];
|
||||
if (![code isEqualToString:@"success"]) {
|
||||
@@ -122,17 +122,17 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 获取并处理解密数据内容
|
||||
// 获取并处理解密数据内<EFBFBD><EFBFBD>?
|
||||
- (id)extractDataContent:(NSDictionary *)json {
|
||||
// 获取mode,判断是否需要解密
|
||||
// 获取mode,判断是否需要解<EFBFBD><EFBFBD>?
|
||||
NSInteger mode = [[json objectForKey:@"mode"] integerValue];
|
||||
id data = [json objectForKey:@"data"];
|
||||
|
||||
if (mode == 1) { // 只处理AES-CBC模式
|
||||
// 需要解密
|
||||
// 需要解<EFBFBD><EFBFBD>?
|
||||
data = [self decryptData:data withMode:mode];
|
||||
} else if (mode != 0) {
|
||||
// 不支持的加密模式(如AES-GCM)
|
||||
// 不支持的加密模式(如AES-GCM<EFBFBD><EFBFBD>?
|
||||
HttpdnsLogDebug("Unsupported encryption mode: %ld", (long)mode);
|
||||
return nil;
|
||||
}
|
||||
@@ -174,7 +174,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 使用工具类解密
|
||||
// 使用工具类解<EFBFBD><EFBFBD>?
|
||||
NSError *decryptError = nil;
|
||||
NSData *decryptedData = [HttpdnsUtil decryptDataAESCBC:encryptedData withKey:keyData error:&decryptError];
|
||||
|
||||
@@ -183,7 +183,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 将解密后的JSON数据解析为字典
|
||||
// 将解密后的JSON数据解析为字<EFBFBD><EFBFBD>?
|
||||
NSError *jsonError;
|
||||
id decodedData = [NSJSONSerialization JSONObjectWithData:decryptedData options:0 error:&jsonError];
|
||||
|
||||
@@ -205,7 +205,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return answers;
|
||||
}
|
||||
|
||||
// 从答案创建主机对象
|
||||
// 从答案创建主机对<EFBFBD><EFBFBD>?
|
||||
- (HttpdnsHostObject *)createHostObjectFromAnswer:(NSDictionary *)answer {
|
||||
// 获取域名
|
||||
NSString *host = [answer objectForKey:@"dn"];
|
||||
@@ -251,7 +251,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
// 设置IPv4的TTL
|
||||
[self setTTLForHostObject:hostObject fromData:v4Data forIPv6:NO];
|
||||
|
||||
// 处理v4的extra字段,优先使用
|
||||
// 处理v4的extra字段,优先使<EFBFBD><EFBFBD>?
|
||||
[self processExtraInfo:v4Data forHostObject:hostObject];
|
||||
|
||||
// 检查是否有no_ip_code字段,表示无IPv4记录
|
||||
@@ -259,7 +259,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
hostObject.hasNoIpv4Record = YES;
|
||||
}
|
||||
} else {
|
||||
// 没有IPv4地址但有v4节点,可能是无记录
|
||||
// 没有IPv4地址但有v4节点,可能是无记<EFBFBD><EFBFBD>?
|
||||
hostObject.hasNoIpv4Record = YES;
|
||||
}
|
||||
}
|
||||
@@ -289,12 +289,12 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
hostObject.hasNoIpv6Record = YES;
|
||||
}
|
||||
} else {
|
||||
// 没有IPv6地址但有v6节点,可能是无记录
|
||||
// 没有IPv6地址但有v6节点,可能是无记<EFBFBD><EFBFBD>?
|
||||
hostObject.hasNoIpv6Record = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置IP数组到主机对象
|
||||
// 设置IP数组到主机对<EFBFBD><EFBFBD>?
|
||||
- (void)setIpArrayToHostObject:(HttpdnsHostObject *)hostObject fromIpsArray:(NSArray *)ips forIPv6:(BOOL)isIPv6 {
|
||||
NSMutableArray *ipArray = [NSMutableArray array];
|
||||
for (NSString *ip in ips) {
|
||||
@@ -352,7 +352,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 准备签名和加密参数
|
||||
// 准备签名和加密参<EFBFBD><EFBFBD>?
|
||||
NSDictionary *paramsToSign = [self prepareSigningParams:request forEncryption:[self shouldUseEncryption]];
|
||||
|
||||
// 计算签名
|
||||
@@ -380,7 +380,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return isV4 ? [scheduleCenter currentActiveServiceServerV4Host] : [scheduleCenter currentActiveServiceServerV6Host];
|
||||
}
|
||||
|
||||
// 检查是否应该使用加密
|
||||
// 检查是否应该使用加<EFBFBD><EFBFBD>?
|
||||
- (BOOL)shouldUseEncryption {
|
||||
HttpDnsService *service = self.service ?: [HttpDnsService sharedInstance];
|
||||
return [HttpdnsUtil isNotEmptyString:service.aesSecretKey];
|
||||
@@ -391,13 +391,13 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
HttpDnsService *service = self.service ?: [HttpDnsService sharedInstance];
|
||||
NSInteger accountId = service.accountID;
|
||||
|
||||
// 构建参与签名的参数字典
|
||||
// 构建参与签名的参数字<EFBFBD><EFBFBD>?
|
||||
NSMutableDictionary *paramsToSign = [NSMutableDictionary dictionary];
|
||||
|
||||
// 构建需要加密的参数字典
|
||||
NSMutableDictionary *paramsToEncrypt = [NSMutableDictionary dictionary];
|
||||
|
||||
// 账号ID,参与签名但不加密
|
||||
// 账号ID,参与签名但不加<EFBFBD><EFBFBD>?
|
||||
[paramsToSign setObject:[NSString stringWithFormat:@"%ld", accountId] forKey:@"id"];
|
||||
|
||||
// 决定加密模式
|
||||
@@ -416,7 +416,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
// SDNS参数,参与签名并加密
|
||||
[self addSdnsParams:request.sdnsParams toParams:paramsToEncrypt];
|
||||
|
||||
// 签名过期时间,参与签名但不加密
|
||||
// 签名过期时间,参与签名但不加<EFBFBD><EFBFBD>?
|
||||
long expiredTimestamp = [self calculateExpiredTimestamp];
|
||||
NSString *expiredTimestampString = [NSString stringWithFormat:@"%ld", expiredTimestamp];
|
||||
[paramsToSign setObject:expiredTimestampString forKey:@"exp"];
|
||||
@@ -435,7 +435,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return paramsToSign;
|
||||
}
|
||||
|
||||
// 获取查询类型字符串
|
||||
// 获取查询类型字符<EFBFBD><EFBFBD>?
|
||||
- (NSString *)getQueryTypeString:(HttpdnsQueryIPType)queryIpType {
|
||||
if ((queryIpType & HttpdnsQueryIPTypeIpv4) && (queryIpType & HttpdnsQueryIPTypeIpv6)) {
|
||||
return @"4,6";
|
||||
@@ -455,7 +455,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 计算过期时间戳
|
||||
// 计算过期时间<EFBFBD><EFBFBD>?
|
||||
- (long)calculateExpiredTimestamp {
|
||||
HttpDnsService *service = self.service ?: [HttpDnsService sharedInstance];
|
||||
long localTimestampOffset = (long)service.authTimeOffset;
|
||||
@@ -510,7 +510,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
[signParts addObject:[NSString stringWithFormat:@"%@=%@", key, [params objectForKey:key]]];
|
||||
}
|
||||
|
||||
// 组合签名字符串
|
||||
// 组合签名字符<EFBFBD><EFBFBD>?
|
||||
NSString *signContent = [signParts componentsJoinedByString:@"&"];
|
||||
|
||||
// 计算HMAC-SHA256签名
|
||||
@@ -538,7 +538,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
// 加密模式下,添加enc参数
|
||||
[finalUrl appendFormat:@"&enc=%@", [params objectForKey:@"enc"]];
|
||||
} else {
|
||||
// 明文模式下,添加所有参数
|
||||
// 明文模式下,添加所有参<EFBFBD><EFBFBD>?
|
||||
NSMutableDictionary *paramsForPlainText = [NSMutableDictionary dictionaryWithDictionary:params];
|
||||
[paramsForPlainText removeObjectForKey:@"id"];
|
||||
[paramsForPlainText removeObjectForKey:@"m"];
|
||||
@@ -557,7 +557,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加签名(如果有)
|
||||
// 添加签名(如果有<EFBFBD><EFBFBD>?
|
||||
if ([HttpdnsUtil isNotEmptyString:signature]) {
|
||||
[finalUrl appendFormat:@"&s=%@", signature];
|
||||
}
|
||||
@@ -568,7 +568,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
return finalUrl;
|
||||
}
|
||||
|
||||
// 添加额外的不参与签名的参数
|
||||
// 添加额外的不参与签名的参<EFBFBD><EFBFBD>?
|
||||
- (void)appendAdditionalParams:(NSMutableString *)url {
|
||||
// sessionId
|
||||
NSString *sessionId = [HttpdnsUtil generateSessionID];
|
||||
@@ -594,7 +594,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
if (!service) {
|
||||
HttpdnsLogDebug("Missing service for accountId: %ld; ensure request.accountId is set and service initialized", (long)request.accountId);
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE userInfo:@{NSLocalizedDescriptionKey: @"HttpDnsService not found for accountId"}];
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE userInfo:@{NSLocalizedDescriptionKey: @"HttpDnsService not found for accountId"}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -612,7 +612,7 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
|
||||
@try {
|
||||
HttpdnsIPStackType stackType = [[HttpdnsIpStackDetector sharedInstance] currentIpStack];
|
||||
// 由于上面默认只用ipv4请求,这里判断如果是ipv6-only环境,那就用v6的ip再试一次
|
||||
// 由于上面默认只用ipv4请求,这里判断如果是ipv6-only环境,那就用v6的ip再试一<EFBFBD><EFBFBD>?
|
||||
if (stackType == kHttpdnsIpv6Only) {
|
||||
url = [self constructHttpdnsResolvingUrl:request forV4Net:NO];
|
||||
HttpdnsLogDebug("lookupHostFromServer by ipv4 server failed, construct ipv6 backup url: %@", url);
|
||||
@@ -631,8 +631,8 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
- (NSArray<HttpdnsHostObject *> *)sendRequest:(NSString *)urlStr queryIpType:(HttpdnsQueryIPType)queryIpType error:(NSError **)error {
|
||||
if (![HttpdnsUtil isNotEmptyString:urlStr]) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Empty resolve URL due to missing scheduler"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -654,8 +654,8 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
if (httpResponse.statusCode != 200) {
|
||||
if (error) {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Unsupported http status code: %ld", (long)httpResponse.statusCode];
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_UNSUPPORTED_STATUS_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_UNSUPPORTED_STATUS_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: errorMessage}];
|
||||
}
|
||||
return nil;
|
||||
@@ -673,8 +673,8 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
NSDictionary *json = [HttpdnsUtil getValidDictionaryFromJson:jsonValue];
|
||||
if (!json) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to parse JSON response"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -691,10 +691,10 @@ static dispatch_queue_t _streamOperateSyncQueue = 0;
|
||||
}
|
||||
|
||||
if ([extra isKindOfClass:[NSString class]]) {
|
||||
// 已经是字符串,直接返回
|
||||
// 已经是字符串,直接返<EFBFBD><EFBFBD>?
|
||||
return extra;
|
||||
} else {
|
||||
// 非字符串,尝试转换为JSON字符串
|
||||
// 非字符串,尝试转换为JSON字符<EFBFBD><EFBFBD>?
|
||||
NSError *error = nil;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:extra options:0 error:&error];
|
||||
if (!error && jsonData) {
|
||||
|
||||
@@ -71,8 +71,8 @@ typedef struct {
|
||||
+ (void)initialize {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_persistentCacheConcurrentQueue = dispatch_queue_create("com.alibaba.sdk.httpdns.persistentCacheOperationQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
_asyncResolveHostQueue = dispatch_queue_create("com.alibaba.sdk.httpdns.asyncResolveHostQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
_persistentCacheConcurrentQueue = dispatch_queue_create("com.Trust.sdk.httpdns.persistentCacheOperationQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
_asyncResolveHostQueue = dispatch_queue_create("com.Trust.sdk.httpdns.asyncResolveHostQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ typedef struct {
|
||||
// 先清理过期时间超过阈值的缓存结果
|
||||
[self->_httpdnsDB cleanRecordAlreadExpiredAt:[[NSDate date] timeIntervalSince1970] - duration];
|
||||
|
||||
// 再读取持久化缓存中的历史记录,加载到内存缓存里
|
||||
// 再读取持久化缓存中的历史记录,加载到内存缓存<EFBFBD><EFBFBD>?
|
||||
[self loadCacheFromDbToMemory];
|
||||
});
|
||||
}
|
||||
@@ -139,7 +139,7 @@ typedef struct {
|
||||
return;
|
||||
}
|
||||
|
||||
// 分批处理,每批最多5个域名
|
||||
// 分批处理,每批最<EFBFBD><EFBFBD>?个域<EFBFBD><EFBFBD>?
|
||||
NSUInteger totalCount = hosts.count;
|
||||
for (NSUInteger i = 0; i < totalCount; i += HTTPDNS_PRE_RESOLVE_BATCH_SIZE) {
|
||||
NSUInteger length = MIN(HTTPDNS_PRE_RESOLVE_BATCH_SIZE, totalCount - i);
|
||||
@@ -190,21 +190,21 @@ typedef struct {
|
||||
if (isCachedResultUsable) {
|
||||
if (isResolvingRequired) {
|
||||
// 缓存结果可用,但是需要请求,因为缓存结果已经过期
|
||||
// 这种情况异步去解析就可以了
|
||||
// 这种情况异步去解析就可以<EFBFBD><EFBFBD>?
|
||||
[self determineResolvingHostNonBlocking:request];
|
||||
}
|
||||
// 缓存是以cacheKey为准,这里返回前,要把host替换成用户请求的这个
|
||||
result.hostName = host;
|
||||
HttpdnsLogDebug("Reuse available cache for cacheKey: %@, result: %@", cacheKey, result);
|
||||
// 因为缓存结果可用,可以立即返回
|
||||
// 因为缓存结果可用,可以立即返<EFBFBD><EFBFBD>?
|
||||
return result;
|
||||
}
|
||||
|
||||
if (request.isBlockingRequest) {
|
||||
// 缓存结果不可用,且是同步请求,需要等待结果
|
||||
// 缓存结果不可用,且是同步请求,需要等待结<EFBFBD><EFBFBD>?
|
||||
return [self determineResolveHostBlocking:request];
|
||||
} else {
|
||||
// 缓存结果不可用,且是异步请求,不需要等待结果
|
||||
// 缓存结果不可用,且是异步请求,不需要等待结<EFBFBD><EFBFBD>?
|
||||
[self determineResolvingHostNonBlocking:request];
|
||||
return nil;
|
||||
}
|
||||
@@ -264,14 +264,14 @@ typedef struct {
|
||||
|
||||
if ([hostObject isExpiredUnderQueryIpType:queryType]) {
|
||||
if (self.atomicExpiredIPEnabled || [hostObject isLoadFromDB]) {
|
||||
// 只有开启了允许过期缓存,和开启持久化缓存情况下启动后加载到内存中的缓存,才可以直接复用过期结果
|
||||
// 只有开启了允许过期缓存,和开启持久化缓存情况下启动后加载到内存中的缓存,才可以直接复用过期结<EFBFBD><EFBFBD>?
|
||||
HttpdnsLogDebug("The ips is expired, but we accept it, host: %@, queryType: %ld, expiredIpEnabled: %d, isLoadFromDB: %d",
|
||||
hostObject.hostName, queryType, self.atomicExpiredIPEnabled, [hostObject isLoadFromDB]);
|
||||
// 复用过期结果,同时也需要发起新的解析请求
|
||||
// 复用过期结果,同时也需要发起新的解析请<EFBFBD><EFBFBD>?
|
||||
return (HostObjectExamingResult){YES, YES};
|
||||
}
|
||||
|
||||
// 只要过期了就肯定需要请求
|
||||
// 只要过期了就肯定需要请<EFBFBD><EFBFBD>?
|
||||
return (HostObjectExamingResult){NO, YES};
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ typedef struct {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 这个路径里,host只会有一个,所以直接取第一个处理就行
|
||||
// 这个路径里,host只会有一个,所以直接取第一个处理就<EFBFBD><EFBFBD>?
|
||||
result = resultArray.firstObject;
|
||||
} else {
|
||||
if (!self.degradeToLocalDNSEnabled) {
|
||||
@@ -332,7 +332,7 @@ typedef struct {
|
||||
|
||||
// merge之后,返回的应当是存储在缓存中的实际对象,而非请求过程中构造出来的对象
|
||||
HttpdnsHostObject *lookupResult = [self mergeLookupResultToManager:result host:host cacheKey:cacheKey underQueryIpType:queryIPType];
|
||||
// 返回一个快照,避免进行中的一些缓存调整影响返回去的结果
|
||||
// 返回一个快照,避免进行中的一些缓存调整影响返回去的结<EFBFBD><EFBFBD>?
|
||||
return [lookupResult copy];
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ typedef struct {
|
||||
HttpdnsNetworkStatus currentStatus = [[HttpdnsReachability sharedInstance] currentReachabilityStatus];
|
||||
NSString *currentStatusString = [[HttpdnsReachability sharedInstance] currentReachabilityString];
|
||||
|
||||
// 重新检测协议栈代价小,所以只要网络切换就发起检测
|
||||
// 重新检测协议栈代价小,所以只要网络切换就发起检<EFBFBD><EFBFBD>?
|
||||
// 但考虑到网络切换后不稳定,还是延迟1秒才发起
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
|
||||
[[HttpdnsIpStackDetector sharedInstance] redetectIpStack];
|
||||
@@ -493,23 +493,23 @@ typedef struct {
|
||||
BOOL statusChanged = (_lastNetworkStatus != currentStatus);
|
||||
|
||||
// 仅在以下情况下响应网络变化去尝试更新缓存:
|
||||
// - 距离上次处理事件至少过去了较长时间,或
|
||||
// - 网络状态发生变化且至少过去了较短时间
|
||||
// - 距离上次处理事件至少过去了较长时间,<EFBFBD><EFBFBD>?
|
||||
// - 网络状态发生变化且至少过去了较短时<EFBFBD><EFBFBD>?
|
||||
NSTimeInterval elapsedTime = currentTimestamp - _lastUpdateTimestamp;
|
||||
if (elapsedTime >= 5 || (statusChanged && elapsedTime >= 1)) {
|
||||
HttpdnsLogDebug("Processing network change: oldStatus: %ld, newStatus: %ld(%@), elapsedTime=%.2f seconds",
|
||||
_lastNetworkStatus, currentStatus, currentStatusString, elapsedTime);
|
||||
|
||||
// 更新调度
|
||||
// 网络在切换过程中可能不稳定,所以发送请求前等待2秒
|
||||
// 网络在切换过程中可能不稳定,所以发送请求前等待2<EFBFBD><EFBFBD>?
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
|
||||
HttpdnsScheduleCenter *scheduleCenter = self.ownerService.scheduleCenter;
|
||||
[scheduleCenter asyncUpdateRegionScheduleConfig];
|
||||
});
|
||||
|
||||
// 复杂逻辑(仅注释关键点,避免冗余):
|
||||
// 网络切换后只预解析“cacheKey 等于 hostName”的条目,
|
||||
// 这些条目代表标准域名缓存;SDNS 等使用自定义 cacheKey 的记录不在此批次处理。
|
||||
// 网络切换后只预解析“cacheKey 等于 hostName”的条目<EFBFBD><EFBFBD>?
|
||||
// 这些条目代表标准域名缓存;SDNS 等使用自定义 cacheKey 的记录不在此批次处理<EFBFBD><EFBFBD>?
|
||||
NSArray *allKeys = [_hostObjectInMemoryCache allCacheKeys];
|
||||
NSMutableArray<NSString *> *hostArray = [NSMutableArray array];
|
||||
for (NSString *key in allKeys) {
|
||||
@@ -524,10 +524,10 @@ typedef struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 预解析
|
||||
// 网络在切换过程中可能不稳定,所以在清理缓存和发送请求前等待3秒
|
||||
// 预解<EFBFBD><EFBFBD>?
|
||||
// 网络在切换过程中可能不稳定,所以在清理缓存和发送请求前等待3<EFBFBD><EFBFBD>?
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
|
||||
// 仅清理“hostName 键”的缓存,保留 SDNS 等自定义 cacheKey 的记录
|
||||
// 仅清理“hostName 键”的缓存,保<EFBFBD><EFBFBD>?SDNS 等自定义 cacheKey 的记<EFBFBD><EFBFBD>?
|
||||
for (NSString *host in hostArray) {
|
||||
[self->_hostObjectInMemoryCache removeHostObjectByCacheKey:host];
|
||||
}
|
||||
@@ -538,7 +538,7 @@ typedef struct {
|
||||
}
|
||||
});
|
||||
|
||||
// 更新时间戳和状态
|
||||
// 更新时间戳和状<EFBFBD><EFBFBD>?
|
||||
_lastNetworkStatus = currentStatus;
|
||||
_lastUpdateTimestamp = currentTimestamp;
|
||||
} else {
|
||||
@@ -552,7 +552,7 @@ typedef struct {
|
||||
|
||||
- (dispatch_queue_t)cacheQueue {
|
||||
if (!_cacheQueue) {
|
||||
_cacheQueue = dispatch_queue_create("com.alibaba.sdk.httpdns.cacheDisableStatusQueue", DISPATCH_QUEUE_SERIAL);
|
||||
_cacheQueue = dispatch_queue_create("com.Trust.sdk.httpdns.cacheDisableStatusQueue", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
return _cacheQueue;
|
||||
}
|
||||
@@ -573,7 +573,7 @@ typedef struct {
|
||||
|
||||
HttpdnsHostObject *hostObject = [HttpdnsHostObject fromDBRecord:hostRecord];
|
||||
|
||||
// 从持久层加载到内存的缓存,需要做个标记,App启动后从缓存使用结果时,根据标记做特殊处理
|
||||
// 从持久层加载到内存的缓存,需要做个标记,App启动后从缓存使用结果时,根据标记做特殊处<EFBFBD><EFBFBD>?
|
||||
[hostObject setIsLoadFromDB:YES];
|
||||
|
||||
[self->_hostObjectInMemoryCache setHostObject:hostObject forCacheKey:cacheKey];
|
||||
@@ -596,7 +596,7 @@ typedef struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 清空数据库数据
|
||||
// 清空数据库数<EFBFBD><EFBFBD>?
|
||||
dispatch_async(_persistentCacheConcurrentQueue, ^{
|
||||
[self->_httpdnsDB deleteByHostNameArr:hostArray];
|
||||
});
|
||||
@@ -605,7 +605,7 @@ typedef struct {
|
||||
- (void)cleanMemoryAndPersistentCacheOfAllHosts {
|
||||
[_hostObjectInMemoryCache removeAllHostObjects];
|
||||
|
||||
// 清空数据库数据
|
||||
// 清空数据库数<EFBFBD><EFBFBD>?
|
||||
dispatch_async(_persistentCacheConcurrentQueue, ^{
|
||||
[self->_httpdnsDB deleteAll];
|
||||
});
|
||||
@@ -622,7 +622,7 @@ typedef struct {
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark - 以下函数仅用于测试目的
|
||||
#pragma mark - 以下函数仅用于测试目<EFBFBD><EFBFBD>?
|
||||
|
||||
- (NSString *)showMemoryCache {
|
||||
NSString *cacheDes;
|
||||
|
||||
@@ -24,19 +24,19 @@
|
||||
// #import "HttpdnsDegradationDelegate.h"
|
||||
// #import "HttpdnsLoggerProtocol.h"
|
||||
|
||||
#import <AlicloudHTTPDNS/HttpdnsRequest.h>
|
||||
#import <AlicloudHTTPDNS/HttpDnsResult.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsLoggerProtocol.h>
|
||||
#import <AlicloudHTTPDNS/HttpdnsDegradationDelegate.h>
|
||||
#import <TrustHTTPDNS/HttpdnsRequest.h>
|
||||
#import <TrustHTTPDNS/HttpDnsResult.h>
|
||||
#import <TrustHTTPDNS/HttpdnsLoggerProtocol.h>
|
||||
#import <TrustHTTPDNS/HttpdnsDegradationDelegate.h>
|
||||
|
||||
#define ALICLOUD_HTTPDNS_DEPRECATED(explain) __attribute__((deprecated(explain)))
|
||||
#define Trust_HTTPDNS_DEPRECATED(explain) __attribute__((deprecated(explain)))
|
||||
|
||||
|
||||
#ifndef ALICLOUDHDNS_STACK_KEY
|
||||
#define ALICLOUDHDNS_STACK_KEY
|
||||
#ifndef TrustHDNS_STACK_KEY
|
||||
#define TrustHDNS_STACK_KEY
|
||||
|
||||
#define ALICLOUDHDNS_IPV4 @"ALICLOUDHDNS_IPV4"
|
||||
#define ALICLOUDHDNS_IPV6 @"ALICLOUDHDNS_IPV6"
|
||||
#define TrustHDNS_IPV4 @"TrustHDNS_IPV4"
|
||||
#define TrustHDNS_IPV6 @"TrustHDNS_IPV6"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -50,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @param host 域名
|
||||
/// @param ipType 当前查询的IP类型
|
||||
/// @param ttl 当次域名解析返回的TTL
|
||||
- (int64_t)httpdnsHost:(NSString * _Nonnull)host ipType:(AlicloudHttpDNS_IPType)ipType ttl:(int64_t)ttl;
|
||||
- (int64_t)httpdnsHost:(NSString * _Nonnull)host ipType:(TrustHttpDNS_IPType)ipType ttl:(int64_t)ttl;
|
||||
|
||||
@end
|
||||
|
||||
@@ -62,154 +62,154 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, copy, readonly, nullable) NSString *aesSecretKey;
|
||||
|
||||
@property (nonatomic, weak, setter=setDelegateForDegradationFilter:) id<HttpDNSDegradationDelegate> delegate ALICLOUD_HTTPDNS_DEPRECATED("不再建议通过设置此回调实现降级逻辑,而是自行在调用HTTPDNS解析域名前做判断");
|
||||
@property (nonatomic, weak, setter=setDelegateForDegradationFilter:) id<HttpDNSDegradationDelegate> delegate Trust_HTTPDNS_DEPRECATED("不再建议通过设置此回调实现降级逻辑,而是自行在调用HTTPDNS解析域名前做判断");
|
||||
|
||||
@property (nonatomic, weak) id<HttpdnsTTLDelegate> ttlDelegate;
|
||||
|
||||
+ (nonnull instancetype)sharedInstance;
|
||||
|
||||
/// 获取指定账号对应的 HttpDnsService 实例
|
||||
/// 获取指定账号对应<EFBFBD><EFBFBD>?HttpDnsService 实例
|
||||
/// @param accountID 账号 ID
|
||||
/// @return 已初始化的实例,若账号尚未注册则返回 nil
|
||||
+ (nullable instancetype)getInstanceByAccountId:(NSInteger)accountID;
|
||||
|
||||
/*!
|
||||
* @brief 无需鉴权功能的初始化接口
|
||||
* @details 初始化,设置 HTTPDNS 服务 Account ID。使用本接口初始化,请求将无任何签名保护,请谨慎使用。
|
||||
* 您可以从控制台获取您的 Account ID 。
|
||||
* 此方法会初始化为单例。
|
||||
* 注意:本接口从3.2.1起废弃,后续将进行移除。
|
||||
* @details 初始化,设置 HTTPDNS 服务 Account ID。使用本接口初始化,请求将无任何签名保护,请谨慎使用<EFBFBD><EFBFBD>?
|
||||
* 您可以从控制台获取您<EFBFBD><EFBFBD>?Account ID <EFBFBD><EFBFBD>?
|
||||
* 此方法会初始化为单例<EFBFBD><EFBFBD>?
|
||||
* 注意:本接口<EFBFBD><EFBFBD>?.2.1起废弃,后续将进行移除<EFBFBD><EFBFBD>?
|
||||
* @param accountID 您的 HTTPDNS Account ID
|
||||
*/
|
||||
- (nonnull instancetype)initWithAccountID:(NSInteger)accountID ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. This method will be removed in the future. Use -[HttpDnsService initWithAccountID:secretKey:] instead.");
|
||||
- (nonnull instancetype)initWithAccountID:(NSInteger)accountID Trust_HTTPDNS_DEPRECATED("Deprecated. This method will be removed in the future. Use -[HttpDnsService initWithAccountID:secretKey:] instead.");
|
||||
|
||||
/*!
|
||||
* @brief 启用鉴权功能的初始化接口
|
||||
* @details 初始化、开启鉴权功能,并设置 HTTPDNS 服务 Account ID,鉴权功能对应的 secretKey。
|
||||
* 您可以从控制台获取您的 Account ID 、secretKey信息。
|
||||
* 此方法会初始化为单例。
|
||||
* @details 初始化、开启鉴权功能,并设<EFBFBD><EFBFBD>?HTTPDNS 服务 Account ID,鉴权功能对应的 secretKey<EFBFBD><EFBFBD>?
|
||||
* 您可以从控制台获取您<EFBFBD><EFBFBD>?Account ID 、secretKey信息<EFBFBD><EFBFBD>?
|
||||
* 此方法会初始化为单例<EFBFBD><EFBFBD>?
|
||||
* @param accountID 您的 HTTPDNS Account ID
|
||||
* @param secretKey 鉴权对应的 secretKey
|
||||
* @param secretKey 鉴权对应<EFBFBD><EFBFBD>?secretKey
|
||||
*/
|
||||
- (nonnull instancetype)initWithAccountID:(NSInteger)accountID secretKey:(NSString * _Nonnull)secretKey;
|
||||
|
||||
/*!
|
||||
* @brief 启用鉴权功能、加密功能的初始化接口
|
||||
* @details 初始化、开启鉴权功能、开启AES加密,并设置 HTTPDNS 服务 Account ID,鉴权功能对应的 secretKey,加密功能对应的 aesSecretKey。
|
||||
* 您可以从控制台获取您的 Account ID 、secretKey、aesSecretKey 信息。
|
||||
* 此方法会初始化为单例。
|
||||
* @brief 启用鉴权功能、加密功能的初始化接<EFBFBD><EFBFBD>?
|
||||
* @details 初始化、开启鉴权功能、开启AES加密,并设置 HTTPDNS 服务 Account ID,鉴权功能对应的 secretKey,加密功能对应的 aesSecretKey<EFBFBD><EFBFBD>?
|
||||
* 您可以从控制台获取您<EFBFBD><EFBFBD>?Account ID 、secretKey、aesSecretKey 信息<EFBFBD><EFBFBD>?
|
||||
* 此方法会初始化为单例<EFBFBD><EFBFBD>?
|
||||
* @param accountID 您的 HTTPDNS Account ID
|
||||
* @param secretKey 鉴权对应的 secretKey
|
||||
* @param aesSecretKey 加密功能对应的 aesSecretKey
|
||||
* @param secretKey 鉴权对应<EFBFBD><EFBFBD>?secretKey
|
||||
* @param aesSecretKey 加密功能对应<EFBFBD><EFBFBD>?aesSecretKey
|
||||
*/
|
||||
- (nonnull instancetype)initWithAccountID:(NSInteger)accountID secretKey:(NSString * _Nonnull)secretKey aesSecretKey:(NSString * _Nullable)aesSecretKey;
|
||||
|
||||
|
||||
/// 开启鉴权功能后,鉴权的签名计算默认读取设备当前时间。若担心设备时间不准确导致签名不准确,可以使用此接口校正 APP 内鉴权计算使用的时间值
|
||||
/// 注意,校正操作在 APP 的一个生命周期内生效,APP 重启后需要重新设置才能重新生效
|
||||
/// @param authCurrentTime 用于校正的时间戳,单位为秒
|
||||
- (void)setAuthCurrentTime:(NSUInteger)authCurrentTime ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setInternalAuthTimeBaseBySpecifyingCurrentTime:] instead.");
|
||||
/// 开启鉴权功能后,鉴权的签名计算默认读取设备当前时间。若担心设备时间不准确导致签名不准确,可以使用此接口校正 APP 内鉴权计算使用的时间<EFBFBD><EFBFBD>?
|
||||
/// 注意,校正操作在 APP 的一个生命周期内生效,APP 重启后需要重新设置才能重新生<EFBFBD><EFBFBD>?
|
||||
/// @param authCurrentTime 用于校正的时间戳,单位为<EFBFBD><EFBFBD>?
|
||||
- (void)setAuthCurrentTime:(NSUInteger)authCurrentTime Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setInternalAuthTimeBaseBySpecifyingCurrentTime:] instead.");
|
||||
|
||||
|
||||
/// 开启鉴权功能后,鉴权的签名计算默认读取设备当前时间。若担心设备时间不准确导致签名不准确,可以使用此接口校正 APP 内鉴权计算使用的时间值
|
||||
/// 注意,校正操作在 APP 的一个生命周期内生效,APP 重启后需要重新设置才能重新生效
|
||||
/// @param currentTime 用于校正的时间戳,单位为秒
|
||||
/// 开启鉴权功能后,鉴权的签名计算默认读取设备当前时间。若担心设备时间不准确导致签名不准确,可以使用此接口校正 APP 内鉴权计算使用的时间<EFBFBD><EFBFBD>?
|
||||
/// 注意,校正操作在 APP 的一个生命周期内生效,APP 重启后需要重新设置才能重新生<EFBFBD><EFBFBD>?
|
||||
/// @param currentTime 用于校正的时间戳,单位为<EFBFBD><EFBFBD>?
|
||||
- (void)setInternalAuthTimeBaseBySpecifyingCurrentTime:(NSTimeInterval)currentTime;
|
||||
|
||||
|
||||
/// 设置持久化缓存功能
|
||||
/// @param enable YES: 开启 NO: 关闭
|
||||
- (void)setCachedIPEnabled:(BOOL)enable ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setPersistentCacheIPEnabled:] instead.");
|
||||
/// 设置持久化缓存功<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setCachedIPEnabled:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setPersistentCacheIPEnabled:] instead.");
|
||||
|
||||
|
||||
/// 设置持久化缓存功能
|
||||
/// 设置持久化缓存功<EFBFBD><EFBFBD>?
|
||||
/// 开启后,每次解析会将结果持久化缓存到本地,当下次应用启动时,可以从本地加载缓存解析结果,提高应用启动时获取解析结果的速度
|
||||
/// 加载时,会丢弃已经过期的解析结果
|
||||
/// @param enable YES: 开启 NO: 关闭
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setPersistentCacheIPEnabled:(BOOL)enable;
|
||||
|
||||
/// 设置持久化缓存功能
|
||||
/// 设置持久化缓存功<EFBFBD><EFBFBD>?
|
||||
/// 开启后,每次解析会将结果持久化缓存到本地,当下次应用启动时,可以从本地加载缓存解析结果,提高应用启动时获取解析结果的速度
|
||||
/// 加载时,会丢弃过期时间已经超过指定值的解析结果
|
||||
/// @param enable YES: 开启 NO: 关闭
|
||||
/// @param duration 决定丢弃IP的过期时间阈值,单位为秒,过期超过这个时间范围的IP会被丢弃,取值范围为0-1年。这个值仅在开启持久化缓存功能时才有意义
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
/// @param duration 决定丢弃IP的过期时间阈值,单位为秒,过期超过这个时间范围的IP会被丢弃,取值范围为0-1年。这个值仅在开启持久化缓存功能时才有意<EFBFBD><EFBFBD>?
|
||||
- (void)setPersistentCacheIPEnabled:(BOOL)enable discardRecordsHasExpiredFor:(NSTimeInterval)duration;
|
||||
|
||||
/// 是否允许 HTTPDNS 返回 TTL 过期域名的 ip ,建议允许(默认不允许)
|
||||
/// @param enable YES: 开启 NO: 关闭
|
||||
- (void)setExpiredIPEnabled:(BOOL)enable ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setReuseExpiredIPEnabled:] instead.");
|
||||
/// 是否允许 HTTPDNS 返回 TTL 过期域名<EFBFBD><EFBFBD>?ip ,建议允许(默认不允许)
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setExpiredIPEnabled:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setReuseExpiredIPEnabled:] instead.");
|
||||
|
||||
|
||||
/// 是否允许 HTTPDNS 返回 TTL 过期域名的 ip ,建议允许(默认不允许)
|
||||
/// @param enable YES: 开启 NO: 关闭
|
||||
/// 是否允许 HTTPDNS 返回 TTL 过期域名<EFBFBD><EFBFBD>?ip ,建议允许(默认不允许)
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setReuseExpiredIPEnabled:(BOOL)enable;
|
||||
|
||||
|
||||
/// 设置 HTTPDNS 域名解析请求类型 ( HTTP / HTTPS )
|
||||
/// 若不调用该接口,默认为 HTTP 请求。
|
||||
/// HTTP 请求基于底层 CFNetwork 实现,不受 ATS 限制;
|
||||
/// 若不调用该接口,默认<EFBFBD><EFBFBD>?HTTP 请求<EFBFBD><EFBFBD>?
|
||||
/// HTTP 请求基于底层 CFNetwork 实现,不<EFBFBD><EFBFBD>?ATS 限制<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: HTTPS请求 NO: HTTP请求
|
||||
- (void)setHTTPSRequestEnabled:(BOOL)enable;
|
||||
|
||||
|
||||
/// 声明App是否配置了ATS为AllowsArbitraryLoads,默认认为没有配置
|
||||
/// 声明App是否配置了ATS为AllowsArbitraryLoads,默认认为没有配<EFBFBD><EFBFBD>?
|
||||
/// 若做了声明,则当指定走HTTP方式解析域名时,解析链路会走系统NSURLSession逻辑
|
||||
/// 否则,会走定制的CFHTTP链路,避免被ATS拦截请求
|
||||
- (void)setHasAllowedArbitraryLoadsInATS:(BOOL)hasAllowedArbitraryLoadsInATS;
|
||||
|
||||
|
||||
/// 设置底层HTTPDNS网络请求超时时间,单位为秒
|
||||
/// 需要注意,这个值只决定底层解析请求的网络超时时间,而非同步解析接口、异步解析接口的最长阻塞或者等待时间
|
||||
/// 设置底层HTTPDNS网络请求超时时间,单位为<EFBFBD><EFBFBD>?
|
||||
/// 需要注意,这个值只决定底层解析请求的网络超时时间,而非同步解析接口、异步解析接口的最长阻塞或者等待时<EFBFBD><EFBFBD>?
|
||||
/// 同步解析接口、异步解析接口的最长阻塞或者等待时间,需要调用接口时设置request参数中的`resolveTimeoutInSecond`决定
|
||||
/// @param timeoutInterval 超时时间,单位为秒
|
||||
/// @param timeoutInterval 超时时间,单位为<EFBFBD><EFBFBD>?
|
||||
- (void)setNetworkingTimeoutInterval:(NSTimeInterval)timeoutInterval;
|
||||
|
||||
|
||||
/// 指定region,指定后会读取该region对应配置作为初始化配置
|
||||
/// 一般情况下无需设置,SDK内部会默认路由全球范围内最近的接入点
|
||||
/// 指定region,指定后会读取该region对应配置作为初始化配<EFBFBD><EFBFBD>?
|
||||
/// 一般情况下无需设置,SDK内部会默认路由全球范围内最近的接入<EFBFBD><EFBFBD>?
|
||||
/// @param region 需要指定的region,缺省为中国大陆
|
||||
- (void)setRegion:(NSString *)region;
|
||||
|
||||
|
||||
/// 域名预解析 (默认解析双栈记录)
|
||||
/// 通常用于启动后立即向SDK设置您后续可能会使用到的热点域名,以便SDK提前解析,减少后续解析域名时请求的时延
|
||||
/// 如果是在运行过程中调用,SDK也会立即解析设置的域名数组中的域名,刷新这些域名的解析结果
|
||||
/// 域名预解<EFBFBD><EFBFBD>?(默认解析双栈记录)
|
||||
/// 通常用于启动后立即向SDK设置您后续可能会使用到的热点域名,以便SDK提前解析,减少后续解析域名时请求的时<EFBFBD><EFBFBD>?
|
||||
/// 如果是在运行过程中调用,SDK也会立即解析设置的域名数组中的域名,刷新这些域名的解析结<EFBFBD><EFBFBD>?
|
||||
///
|
||||
/// @param hosts 预解析列表数组
|
||||
/// @param hosts 预解析列表数<EFBFBD><EFBFBD>?
|
||||
- (void)setPreResolveHosts:(NSArray *)hosts;
|
||||
|
||||
|
||||
/// 域名预解析,可以指定预解析auto、ipv4、ipv6、both
|
||||
/// 通常用于启动后立即向SDK设置您后续可能会使用到的热点域名,以便SDK提前解析,减少后续解析域名时请求的时延
|
||||
/// 如果是在运行过程中调用,SDK也会立即解析设置的域名数组中的域名,刷新这些域名的解析结果
|
||||
/// 通常用于启动后立即向SDK设置您后续可能会使用到的热点域名,以便SDK提前解析,减少后续解析域名时请求的时<EFBFBD><EFBFBD>?
|
||||
/// 如果是在运行过程中调用,SDK也会立即解析设置的域名数组中的域名,刷新这些域名的解析结<EFBFBD><EFBFBD>?
|
||||
///
|
||||
/// @param hosts 预解析列表数组
|
||||
/// @param ipType 指定预解析记录类型
|
||||
/// @param hosts 预解析列表数<EFBFBD><EFBFBD>?
|
||||
/// @param ipType 指定预解析记录类<EFBFBD><EFBFBD>?
|
||||
- (void)setPreResolveHosts:(NSArray *)hosts byIPType:(HttpdnsQueryIPType)ipType;
|
||||
|
||||
|
||||
/// 域名预解析
|
||||
/// 域名预解<EFBFBD><EFBFBD>?
|
||||
/// @param hosts 域名
|
||||
/// @param ipType 4: ipv4; 6: ipv6; 64: ipv4+ipv6
|
||||
- (void)setPreResolveHosts:(NSArray *)hosts queryIPType:(AlicloudHttpDNS_IPType)ipType ALICLOUD_HTTPDNS_DEPRECATED("Deprecated, this method will be removed in the future. Use -[HttpDnsService setPreResolveHosts:byIPType:] instead.");
|
||||
- (void)setPreResolveHosts:(NSArray *)hosts queryIPType:(TrustHttpDNS_IPType)ipType Trust_HTTPDNS_DEPRECATED("Deprecated, this method will be removed in the future. Use -[HttpDnsService setPreResolveHosts:byIPType:] instead.");
|
||||
|
||||
|
||||
/// 本地日志 log 开关
|
||||
/// 本地日志 log 开<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: 打开 NO: 关闭
|
||||
- (void)setLogEnabled:(BOOL)enable;
|
||||
|
||||
|
||||
/// 设置网络切换时是否自动更新所有域名解析结果
|
||||
/// 如果打开此开关,在网络切换时,会自动刷新所有域名的解析结果,但会产生一定流量消耗
|
||||
/// @param enable YES: 开启 NO: 关闭
|
||||
/// 设置网络切换时是否自动更新所有域名解析结<EFBFBD><EFBFBD>?
|
||||
/// 如果打开此开关,在网络切换时,会自动刷新所有域名的解析结果,但会产生一定流量消<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setPreResolveAfterNetworkChanged:(BOOL)enable;
|
||||
|
||||
|
||||
/// 设置当httpdns解析失败时是否降级到localDNS尝试解析
|
||||
/// 降级生效时,SDNS参数不生效,降级逻辑只解析域名,返回的结果默认使用60秒(若未指定该域名自定义TTL)作为TTL值
|
||||
/// 降级生效时,SDNS参数不生效,降级逻辑只解析域名,返回的结果默认使<EFBFBD><EFBFBD>?0<><30>?若未指定该域名自定义TTL)作为TTL<EFBFBD><EFBFBD>?
|
||||
/// 降级请求也不会再对ip进行优先排序
|
||||
/// 默认关闭,不会自动降级
|
||||
/// @param enable YES:自动降级 NO:不自动降级
|
||||
/// 默认关闭,不会自动降<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES:自动降<EFBFBD><EFBFBD>?NO:不自动降级
|
||||
- (void)setDegradeToLocalDNSEnabled:(BOOL)enable;
|
||||
|
||||
|
||||
@@ -219,59 +219,59 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)setIPRankingDatasource:(NSDictionary<NSString *, NSNumber *> *)IPRankingDatasource;
|
||||
|
||||
|
||||
/// 设置是否 开启 IPv6 结果解析。只有开启状态下,对域名的解析才会尝试解析v6记录并返回v6的结果
|
||||
/// @param enable YES: 开启 NO: 关闭
|
||||
- (void)enableIPv6:(BOOL)enable ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setIPv6Enabled:] instead.");
|
||||
/// 设置是否 开<EFBFBD><EFBFBD>?IPv6 结果解析。只有开启状态下,对域名的解析才会尝试解析v6记录并返回v6的结<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)enableIPv6:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setIPv6Enabled:] instead.");
|
||||
|
||||
|
||||
/// 设置是否 开启 IPv6 结果解析。只有开启状态下,对域名的解析才会尝试解析v6记录并返回v6的结果
|
||||
/// 设置是否 开<EFBFBD><EFBFBD>?IPv6 结果解析。只有开启状态下,对域名的解析才会尝试解析v6记录并返回v6的结<EFBFBD><EFBFBD>?
|
||||
/// 已弃用。默认支持IPv6。如果不需要IPv6类型的结果,只需在请求时指定`queryIpType`为`HttpdnsQueryIPTypeIpv4`
|
||||
/// @param enable YES: 开启 NO: 关闭
|
||||
- (void)setIPv6Enabled:(BOOL)enable ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. If ipv6 is unnecessary, you can set the `queryIpType` as HttpdnsQueryIPTypeIpv4 when resolving domain.");
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setIPv6Enabled:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. If ipv6 is unnecessary, you can set the `queryIpType` as HttpdnsQueryIPTypeIpv4 when resolving domain.");
|
||||
|
||||
|
||||
/// 是否允许通过 CNCopyCurrentNetworkInfo 获取wifi ssid bssid
|
||||
/// @param enable YES: 开启 NO: 关闭 ,默认关闭
|
||||
- (void)enableNetworkInfo:(BOOL)enable ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. We do not utilize network information anymore");
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭 ,默认关<EFBFBD><EFBFBD>?
|
||||
- (void)enableNetworkInfo:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. We do not utilize network information anymore");
|
||||
|
||||
|
||||
/// 是否允许通过 CNCopyCurrentNetworkInfo 获取wifi ssid bssid
|
||||
/// @param enable YES: 开启 NO: 关闭 ,默认关闭
|
||||
- (void)setReadNetworkInfoEnabled:(BOOL)enable ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. We do not utilize network information anymore.");
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭 ,默认关<EFBFBD><EFBFBD>?
|
||||
- (void)setReadNetworkInfoEnabled:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. We do not utilize network information anymore.");
|
||||
|
||||
|
||||
/// 是否开启IP探测功能
|
||||
/// @param enable YES: 开启 NO: 关闭 默认打开
|
||||
- (void)enableCustomIPRank:(BOOL)enable ALICLOUD_HTTPDNS_DEPRECATED("Deprecated, will be removed in the future.");
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭 默认打开
|
||||
- (void)enableCustomIPRank:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated, will be removed in the future.");
|
||||
|
||||
|
||||
/// 设置软件自定义解析全局默认参数,设置后,调用软件自定义解析时,每个请求默认都会带上这里配置的参数
|
||||
/// 设置软件自定义解析全局默认参数,设置后,调用软件自定义解析时,每个请求默认都会带上这里配置的参<EFBFBD><EFBFBD>?
|
||||
/// @param params 全局默认参数
|
||||
- (void)setSdnsGlobalParams:(NSDictionary<NSString *, NSString *> *)params;
|
||||
|
||||
|
||||
/// 设置日志输出回调,以实现自定义日志输出方式
|
||||
/// 设置日志输出回调,以实现自定义日志输出方<EFBFBD><EFBFBD>?
|
||||
/// @param logHandler 日志输出回调实现实例
|
||||
- (void)setLogHandler:(id<HttpdnsLoggerProtocol>)logHandler;
|
||||
|
||||
|
||||
/// 获取用于用户追踪的 sessionId
|
||||
/// sessionId为随机生成,长度为 12 位,App 生命周期内保持不变
|
||||
/// 为了排查可能的解析问题,需要您将 sessionId 和解析出的 IP 一起记录在日志中
|
||||
/// 请参考: 解析异常排查之 “会话追踪方案” https://help.aliyun.com/document_detail/100530.html
|
||||
/// 获取用于用户追踪<EFBFBD><EFBFBD>?sessionId
|
||||
/// sessionId为随机生成,长度<EFBFBD><EFBFBD>?12 位,App 生命周期内保持不<EFBFBD><EFBFBD>?
|
||||
/// 为了排查可能的解析问题,需要您<EFBFBD><EFBFBD>?sessionId 和解析出<EFBFBD><EFBFBD>?IP 一起记录在日志<EFBFBD><EFBFBD>?
|
||||
/// 请参<EFBFBD><EFBFBD>? 解析异常排查<EFBFBD><EFBFBD>?“会话追踪方案<EFBFBD><EFBFBD>?https://help.TrustAPP.com/document_detail/100530.html
|
||||
- (NSString *)getSessionId;
|
||||
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结果
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑。
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结<EFBFBD><EFBFBD>?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑<EFBFBD><EFBFBD>?
|
||||
/// @param host 需要解析的域名
|
||||
/// @param queryIpType 可设置为自动选择,ipv4,ipv6. 设置为自动选择时,会自动根据当前所处网络环境选择解析ipv4或ipv6
|
||||
/// @return 解析结果
|
||||
- (nullable HttpdnsResult *)resolveHostSync:(NSString *)host byIpType:(HttpdnsQueryIPType)queryIpType;
|
||||
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结果
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑。
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结<EFBFBD><EFBFBD>?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑<EFBFBD><EFBFBD>?
|
||||
/// @param host 需要解析的域名
|
||||
/// @param queryIpType 可设置为自动选择,ipv4,ipv6. 设置为自动选择时,会自动根据当前所处网络环境选择解析ipv4或ipv6
|
||||
/// @param sdnsParams 如果域名配置了sdns自定义解析,通过此参数携带自定义参数
|
||||
@@ -279,22 +279,22 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @return 解析结果
|
||||
- (nullable HttpdnsResult *)resolveHostSync:(NSString *)host byIpType:(HttpdnsQueryIPType)queryIpType withSdnsParams:(NSDictionary<NSString *, NSString *> *)sdnsParams sdnsCacheKey:(NSString *)cacheKey;
|
||||
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结果
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑。
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结<EFBFBD><EFBFBD>?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑<EFBFBD><EFBFBD>?
|
||||
/// @param request 请求参数对象
|
||||
/// @return 解析结果
|
||||
- (nullable HttpdnsResult *)resolveHostSync:(HttpdnsRequest *)request;
|
||||
|
||||
/// 异步解析域名,不会阻塞当前线程,会在从缓存中获取到有效结果,或从服务器拿到最新解析结果后,通过回调返回结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先在回调中返回这个结果,然后启动后台线程去更新解析结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先在回调中返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// @param host 需要解析的域名
|
||||
/// @param queryIpType 可设置为自动选择,ipv4,ipv6. 设置为自动选择时,会自动根据当前所处网络环境选择解析ipv4或ipv6
|
||||
/// @handler 解析结果回调
|
||||
- (void)resolveHostAsync:(NSString *)host byIpType:(HttpdnsQueryIPType)queryIpType completionHandler:(void (^)(HttpdnsResult * nullable))handler;
|
||||
|
||||
/// 异步解析域名,不会阻塞当前线程,会在从缓存中获取到有效结果,或从服务器拿到最新解析结果后,通过回调返回结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先在回调中返回这个结果,然后启动后台线程去更新解析结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先在回调中返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// @param host 需要解析的域名
|
||||
/// @param queryIpType 可设置为自动选择,ipv4,ipv6. 设置为自动选择时,会自动根据当前所处网络环境选择解析ipv4或ipv6
|
||||
/// @param sdnsParams 如果域名配置了sdns自定义解析,通过此参数携带自定义参数
|
||||
@@ -303,20 +303,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)resolveHostAsync:(NSString *)host byIpType:(HttpdnsQueryIPType)queryIpType withSdnsParams:(nullable NSDictionary<NSString *, NSString *> *)sdnsParams sdnsCacheKey:(nullable NSString *)cacheKey completionHandler:(void (^)(HttpdnsResult * nullable))handler;
|
||||
|
||||
/// 异步解析域名,不会阻塞当前线程,会在从缓存中获取到有效结果,或从服务器拿到最新解析结果后,通过回调返回结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先在回调中返回这个结果,然后启动后台线程去更新解析结果
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先在回调中返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// @param request 请求参数对象
|
||||
/// @handler 解析结果回调
|
||||
- (void)resolveHostAsync:(HttpdnsRequest *)request completionHandler:(void (^)(HttpdnsResult * nullable))handler;
|
||||
|
||||
/// 伪异步解析域名,不会阻塞当前线程,首次解析结果可能为空
|
||||
/// 先查询缓存,缓存中存在有效结果(未过期,或者过期但配置了可以复用过期解析结果),则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 伪异步解析域名,不会阻塞当前线程,首次解析结果可能为<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在有效结<EFBFBD><EFBFBD>?未过期,或者过期但配置了可以复用过期解析结<EFBFBD><EFBFBD>?,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// @param host 需要解析的域名
|
||||
/// @param queryIpType 可设置为自动选择,ipv4,ipv6. 设置为自动选择时,会自动根据当前所处网络环境选择解析ipv4或ipv6
|
||||
/// @return 解析结果
|
||||
- (nullable HttpdnsResult *)resolveHostSyncNonBlocking:(NSString *)host byIpType:(HttpdnsQueryIPType)queryIpType;
|
||||
|
||||
/// 伪异步解析域名,不会阻塞当前线程,首次解析结果可能为空
|
||||
/// 先查询缓存,缓存中存在有效结果(未过期,或者过期但配置了可以复用过期解析结果),则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 伪异步解析域名,不会阻塞当前线程,首次解析结果可能为<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在有效结<EFBFBD><EFBFBD>?未过期,或者过期但配置了可以复用过期解析结<EFBFBD><EFBFBD>?,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// @param host 需要解析的域名
|
||||
/// @param queryIpType 可设置为自动选择,ipv4,ipv6. 设置为自动选择时,会自动根据当前所处网络环境选择解析ipv4或ipv6
|
||||
/// @param sdnsParams 如果域名配置了sdns自定义解析,通过此参数携带自定义参数
|
||||
@@ -324,8 +324,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @return 解析结果
|
||||
- (nullable HttpdnsResult *)resolveHostSyncNonBlocking:(NSString *)host byIpType:(HttpdnsQueryIPType)queryIpType withSdnsParams:(nullable NSDictionary<NSString *, NSString *> *)sdnsParams sdnsCacheKey:(nullable NSString *)cacheKey;
|
||||
|
||||
/// 伪异步解析域名,不会阻塞当前线程,首次解析结果可能为空
|
||||
/// 先查询缓存,缓存中存在有效结果(未过期,或者过期但配置了可以复用过期解析结果),则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 伪异步解析域名,不会阻塞当前线程,首次解析结果可能为<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在有效结<EFBFBD><EFBFBD>?未过期,或者过期但配置了可以复用过期解析结<EFBFBD><EFBFBD>?,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// @param request 请求参数对象
|
||||
/// @return 解析结果
|
||||
- (nullable HttpdnsResult *)resolveHostSyncNonBlocking:(HttpdnsRequest *)request;
|
||||
@@ -333,34 +333,34 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// 获取域名对应的IP,单IP
|
||||
/// @param host 域名
|
||||
- (NSString *)getIpByHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSString *)getIpByHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的IP数组,多IP
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIpsByHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSArray *)getIpsByHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6, 单IP (需要开启ipv6 开关 enableIPv6)
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6, 单IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSString *)getIPv6ByHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSString *)getIPv6ByHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6数组, 多IP (需要开启ipv6 开关 enableIPv6)
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6数组, 多IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv6sByHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSArray *)getIPv6sByHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 同时获取ipv4 ipv6的IP (需要开启ipv6 开关 enableIPv6)
|
||||
/// 同时获取ipv4 ipv6的IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// ALICLOUDHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// ALICLOUDHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// TrustHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// TrustHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// }
|
||||
- (NSDictionary <NSString *, NSArray *>*)getIPv4_v6ByHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSDictionary <NSString *, NSArray *>*)getIPv4_v6ByHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址组
|
||||
/// 使用此API 需要确保 enableIPv6 开关已打开
|
||||
/// 根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址<EFBFBD><EFBFBD>?
|
||||
/// 使用此API 需要确<EFBFBD><EFBFBD>?enableIPv6 开关已打开
|
||||
/// 设备网络 返回域名IP
|
||||
/// IPv4 Only IPv4
|
||||
/// IPv6 Only IPv6 (如果没有Pv6返回空)
|
||||
@@ -368,75 +368,75 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @param host 要解析的域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// ALICLOUDHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// ALICLOUDHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// TrustHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// TrustHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// }
|
||||
-(NSDictionary <NSString *, NSArray *>*)autoGetIpsByHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
-(NSDictionary <NSString *, NSArray *>*)autoGetIpsByHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的IPv4地址,单IPv4
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSString *)getIPv4ForHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSString *)getIPv4ForHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的IP数组,多IP
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv4ListForHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSArray *)getIPv4ListForHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 获取IPv4地址列表,同步接口,必须在子线程中执行,否则会转变为异步接口
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限为5s,
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大于5s,同步接口也最多阻塞当前线程5s
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起同步解析请求
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限<EFBFBD><EFBFBD>?s<><73>?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大<EFBFBD><EFBFBD>?s,同步接口也最多阻塞当前线<EFBFBD><EFBFBD>?s
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起同步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv4ListForHostSync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
- (NSArray *)getIPv4ListForHostSync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6, 单IP (需要开启ipv6 开关 enableIPv6)
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6, 单IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSString *)getIPv6ForHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSString *)getIPv6ForHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6数组, 多IP (需要开启ipv6 开关 enableIPv6)
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6数组, 多IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv6ListForHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSArray *)getIPv6ListForHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 获取IPv6地址列表,同步接口,必须在子线程中执行,否则会转变为异步接口
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限为5s,
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大于5s,同步接口也最多阻塞当前线程5s
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限<EFBFBD><EFBFBD>?s<><73>?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大<EFBFBD><EFBFBD>?s,同步接口也最多阻塞当前线<EFBFBD><EFBFBD>?s
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv6ListForHostSync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
- (NSArray *)getIPv6ListForHostSync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应格式化后的IP (针对ipv6)
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
- (NSString *)getIpByHostAsyncInURLFormat:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSString *)getIpByHostAsyncInURLFormat:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
|
||||
/// 异步接口,首次结果可能为空,同时获取ipv4 ipv6的IP (需要开启ipv6 开关 enableIPv6)
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 异步接口,首次结果可能为空,同时获取ipv4 ipv6的IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// ALICLOUDHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// ALICLOUDHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// TrustHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// TrustHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// }
|
||||
- (NSDictionary <NSString *, NSArray *>*)getHttpDnsResultHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSDictionary <NSString *, NSArray *>*)getHttpDnsResultHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// NOTE: 同步接口,必须在子线程中执行,否则会转变为异步接口
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限为5s,
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大于5s,同步接口也最多阻塞当前线程5s
|
||||
/// 同时获取ipv4 + ipv6的IP (需要开启ipv6 开关 enableIPv6)
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// NOTE: 同步接口,必须在子线程中执行,否则会转变为异步接<EFBFBD><EFBFBD>?
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限<EFBFBD><EFBFBD>?s<><73>?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大<EFBFBD><EFBFBD>?s,同步接口也最多阻塞当前线<EFBFBD><EFBFBD>?s
|
||||
/// 同时获取ipv4 + ipv6的IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// ALICLOUDHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// ALICLOUDHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// TrustHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// TrustHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// }
|
||||
- (NSDictionary <NSString *, NSArray *>*)getHttpDnsResultHostSync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
- (NSDictionary <NSString *, NSArray *>*)getHttpDnsResultHostSync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址组
|
||||
/// 使用此API 需要确保 enableIPv6 开关已打开
|
||||
/// 异步接口,首次结果可能为空,根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址<EFBFBD><EFBFBD>?
|
||||
/// 使用此API 需要确<EFBFBD><EFBFBD>?enableIPv6 开关已打开
|
||||
/// 设备网络 返回域名IP
|
||||
/// IPv4 Only IPv4
|
||||
/// IPv6 Only IPv6 (如果没有Pv6返回空)
|
||||
@@ -444,33 +444,33 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @param host 要解析的域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// ALICLOUDHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// ALICLOUDHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// TrustHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// TrustHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// }
|
||||
-(NSDictionary <NSString *, NSArray *>*)autoGetHttpDnsResultForHostAsync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
-(NSDictionary <NSString *, NSArray *>*)autoGetHttpDnsResultForHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址组,同步接口,必须在子线程中执行,否则会转变为异步接口
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限为5s,
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大于5s,同步接口也最多阻塞当前线程5s
|
||||
/// 根据当前网络栈自动获取ipv4 ipv6的IP (需要开启ipv6 开关 enableIPv6)
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请求
|
||||
/// 根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址组,同步接口,必须在子线程中执行,否则会转变为异步接<EFBFBD><EFBFBD>?
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限<EFBFBD><EFBFBD>?s<><73>?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大<EFBFBD><EFBFBD>?s,同步接口也最多阻塞当前线<EFBFBD><EFBFBD>?s
|
||||
/// 根据当前网络栈自动获取ipv4 ipv6的IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// @param host 域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// ALICLOUDHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// ALICLOUDHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// TrustHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// TrustHDNS_IPV6: ['xx:xx:xx:xx:xx:xx:xx:xx', 'xx:xx:xx:xx:xx:xx:xx:xx']
|
||||
/// }
|
||||
- (NSDictionary <NSString *, NSArray *>*)autoGetHttpDnsResultForHostSync:(NSString *)host ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
- (NSDictionary <NSString *, NSArray *>*)autoGetHttpDnsResultForHostSync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
|
||||
/// 软件自定义解析接口
|
||||
- (NSDictionary *)getIpsByHostAsync:(NSString *)host withParams:(NSDictionary<NSString *, NSString *> *)params withCacheKey:(NSString *)cacheKey ALICLOUD_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:withSdnsParams:sdnsCacheKey:] instead.");
|
||||
/// 软件自定义解析接<EFBFBD><EFBFBD>?
|
||||
- (NSDictionary *)getIpsByHostAsync:(NSString *)host withParams:(NSDictionary<NSString *, NSString *> *)params withCacheKey:(NSString *)cacheKey Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:withSdnsParams:sdnsCacheKey:] instead.");
|
||||
|
||||
|
||||
/// 清除指定host缓存(内存+沙盒数据库)
|
||||
/// 清除指定host缓存(内<EFBFBD><EFBFBD>?沙盒数据库)
|
||||
/// @param hostArray 需要清除的host域名数组。如果需要清空全部数据传nil或者空数组即可
|
||||
- (void)cleanHostCache:(nullable NSArray<NSString *> *)hostArray;
|
||||
|
||||
/// 清除当前所有host缓存 (内存+沙盒数据库)
|
||||
/// 清除当前所有host缓存 (内存+沙盒数据<EFBFBD><EFBFBD>?
|
||||
- (void)cleanAllHostCache;
|
||||
|
||||
/// 清理已经配置的软件自定义解析全局参数
|
||||
|
||||
@@ -44,12 +44,12 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
@property (nonatomic, copy) NSString *aesSecretKey;
|
||||
@property (nonatomic, assign) BOOL hasConfiguredAccount;
|
||||
|
||||
// 每次访问的签名有效期,SDK内部定死,当前不暴露设置接口,有效期定为10分钟。
|
||||
// 每次访问的签名有效期,SDK内部定死,当前不暴露设置接口,有效期定为10分钟<EFBFBD><EFBFBD>?
|
||||
@property (nonatomic, assign) NSUInteger authTimeoutInterval;
|
||||
|
||||
@property (nonatomic, copy) NSDictionary<NSString *, NSString *> *presetSdnsParamsDict;
|
||||
|
||||
// scheduleCenter 已在 HttpdnsService_Internal.h 暴露,避免重复声明
|
||||
// scheduleCenter 已在 HttpdnsService_Internal.h 暴露,避免重复声<EFBFBD><EFBFBD>?
|
||||
|
||||
@end
|
||||
|
||||
@@ -58,9 +58,9 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
+ (void)initialize {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
asyncTaskConcurrentQueue = dispatch_queue_create("com.alibaba.sdk.httpdns.asyncTask", DISPATCH_QUEUE_CONCURRENT);
|
||||
asyncTaskConcurrentQueue = dispatch_queue_create("com.Trust.sdk.httpdns.asyncTask", DISPATCH_QUEUE_CONCURRENT);
|
||||
httpdnsServiceInstances = [NSMutableDictionary dictionary];
|
||||
httpdnsServiceInstancesQueue = dispatch_queue_create("com.alibaba.sdk.httpdns.serviceRegistry", DISPATCH_QUEUE_SERIAL);
|
||||
httpdnsServiceInstancesQueue = dispatch_queue_create("com.Trust.sdk.httpdns.serviceRegistry", DISPATCH_QUEUE_SERIAL);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
self.requestManager = [[HttpdnsRequestManager alloc] initWithAccountId:accountID ownerService:self];
|
||||
|
||||
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
|
||||
NSString *regionKey = [NSString stringWithFormat:@"%@.%ld", kAlicloudHttpdnsRegionKey, (long)accountID];
|
||||
NSString *regionKey = [NSString stringWithFormat:@"%@.%ld", kTrustHttpdnsRegionKey, (long)accountID];
|
||||
NSString *cachedRegion = [userDefault objectForKey:regionKey];
|
||||
|
||||
HttpdnsScheduleCenter *scheduleCenter = [[HttpdnsScheduleCenter alloc] initWithAccountId:accountID];
|
||||
@@ -235,7 +235,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
|
||||
- (void)setRegion:(NSString *)region {
|
||||
if ([HttpdnsUtil isEmptyString:region]) {
|
||||
region = ALICLOUD_HTTPDNS_DEFAULT_REGION_KEY;
|
||||
region = Trust_HTTPDNS_DEFAULT_REGION_KEY;
|
||||
}
|
||||
|
||||
if (![[HttpdnsRegionConfigLoader getAvailableRegionList] containsObject:region]) {
|
||||
@@ -244,7 +244,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
}
|
||||
|
||||
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
|
||||
NSString *regionKey = [NSString stringWithFormat:@"%@.%ld", kAlicloudHttpdnsRegionKey, (long)self.accountID];
|
||||
NSString *regionKey = [NSString stringWithFormat:@"%@.%ld", kTrustHttpdnsRegionKey, (long)self.accountID];
|
||||
NSString *oldRegion = [userDefault objectForKey:regionKey];
|
||||
if (![region isEqualToString:oldRegion]) {
|
||||
[userDefault setObject:region forKey:regionKey];
|
||||
@@ -262,16 +262,16 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
}
|
||||
|
||||
|
||||
- (void)setPreResolveHosts:(NSArray *)hosts queryIPType:(AlicloudHttpDNS_IPType)ipType {
|
||||
- (void)setPreResolveHosts:(NSArray *)hosts queryIPType:(TrustHttpDNS_IPType)ipType {
|
||||
HttpdnsQueryIPType ipQueryType;
|
||||
switch (ipType) {
|
||||
case AlicloudHttpDNS_IPTypeV4:
|
||||
case TrustHttpDNS_IPTypeV4:
|
||||
ipQueryType = HttpdnsQueryIPTypeIpv4;
|
||||
break;
|
||||
case AlicloudHttpDNS_IPTypeV6:
|
||||
case TrustHttpDNS_IPTypeV6:
|
||||
ipQueryType = HttpdnsQueryIPTypeIpv6;
|
||||
break;
|
||||
case AlicloudHttpDNS_IPTypeV64:
|
||||
case TrustHttpDNS_IPTypeV64:
|
||||
ipQueryType = HttpdnsQueryIPTypeIpv4 | HttpdnsQueryIPTypeIpv6;
|
||||
break;
|
||||
default:
|
||||
@@ -284,7 +284,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
|
||||
- (void)setPreResolveHosts:(NSArray *)hosts byIPType:(HttpdnsQueryIPType)ipType {
|
||||
// 初始化过程包含了region配置更新流程,region切换会导致缓存清空,立即做预解析可能是没有意义的
|
||||
// 这是sdk接口设计的历史问题,目前没有太好办法,这里0.5秒之后再发预解析请求
|
||||
// 这是sdk接口设计的历史问题,目前没有太好办法,这<EFBFBD><EFBFBD>?.5秒之后再发预解析请求
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), asyncTaskConcurrentQueue, ^{
|
||||
[self->_requestManager preResolveHosts:hosts queryType:ipType];
|
||||
});
|
||||
@@ -320,20 +320,20 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
}
|
||||
|
||||
- (void)setIPv6Enabled:(BOOL)enable {
|
||||
// 默认都支持
|
||||
// 默认都支<EFBFBD><EFBFBD>?
|
||||
}
|
||||
|
||||
- (void)enableNetworkInfo:(BOOL)enable {
|
||||
// 弃用此接口
|
||||
// 弃用此接<EFBFBD><EFBFBD>?
|
||||
}
|
||||
|
||||
- (void)setReadNetworkInfoEnabled:(BOOL)enable {
|
||||
// 弃用此接口
|
||||
// 弃用此接<EFBFBD><EFBFBD>?
|
||||
}
|
||||
|
||||
- (void)enableCustomIPRank:(BOOL)enable {
|
||||
// 不再生效,保留接口
|
||||
// 是否开启自定义IP排序,由是否设置IPRankingDatasource和IPRankingDatasource中是否能根据host找到对应的IP来决定
|
||||
// 不再生效,保留接<EFBFBD><EFBFBD>?
|
||||
// 是否开启自定义IP排序,由是否设置IPRankingDatasource和IPRankingDatasource中是否能根据host找到对应的IP来决<EFBFBD><EFBFBD>?
|
||||
}
|
||||
|
||||
- (NSString *)getSessionId {
|
||||
@@ -357,7 +357,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
|
||||
- (nullable HttpdnsResult *)resolveHostSync:(HttpdnsRequest *)request {
|
||||
if ([NSThread isMainThread]) {
|
||||
// 主线程做一个防御
|
||||
// 主线程做一个防<EFBFBD><EFBFBD>?
|
||||
return [self resolveHostSyncNonBlocking:request];
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
if (specifiedQueryIpType == HttpdnsQueryIPTypeAuto) {
|
||||
HttpdnsIPStackType stackType = [[HttpdnsIpStackDetector sharedInstance] currentIpStack];
|
||||
switch (stackType) {
|
||||
// 双栈和ipv6only,两个类型都要请求
|
||||
// 双栈和ipv6only,两个类型都要请<EFBFBD><EFBFBD>?
|
||||
// 虽然判断是ipv6only,但现实中只有实验室才会有这种情况,考虑判断网络协议栈是有误判可能的,权衡之下,还是应该请求ipv4
|
||||
// 如果用户是在明确的实验室环境中做测试,他应该直接指定请求type为ipv6
|
||||
case kHttpdnsIpDual:
|
||||
@@ -470,7 +470,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
}
|
||||
}
|
||||
|
||||
// 否则就按指定类型来解析
|
||||
// 否则就按指定类型来解<EFBFBD><EFBFBD>?
|
||||
return specifiedQueryIpType;
|
||||
}
|
||||
|
||||
@@ -681,7 +681,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
}
|
||||
//需要检查是不是在主线程,如果是主线程,保持异步逻辑
|
||||
if ([NSThread isMainThread]) {
|
||||
//如果是主线程,仍然使用异步的方式,即先查询缓存,如果没有,则发送异步请求
|
||||
//如果是主线程,仍然使用异步的方式,即先查询缓存,如果没有,则发送异步请<EFBFBD><EFBFBD>?
|
||||
HttpdnsRequest *request = [[HttpdnsRequest alloc] initWithHost:host queryIpType:HttpdnsQueryIPTypeIpv4];
|
||||
[self attachAccountInfoToRequest:request];
|
||||
[request becomeNonBlockingRequest];
|
||||
@@ -839,7 +839,7 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
}
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
// 如果是主线程,仍然使用异步的方式,即先查询缓存,如果没有,则发送异步请求
|
||||
// 如果是主线程,仍然使用异步的方式,即先查询缓存,如果没有,则发送异步请<EFBFBD><EFBFBD>?
|
||||
HttpdnsRequest *request = [[HttpdnsRequest alloc] initWithHost:host queryIpType:HttpdnsQueryIPTypeIpv6];
|
||||
[self attachAccountInfoToRequest:request];
|
||||
[request becomeNonBlockingRequest];
|
||||
@@ -888,9 +888,9 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
if ([HttpdnsUtil isAnIP:host]) {
|
||||
HttpdnsLogDebug("The host is just an IP: %@", host);
|
||||
if ([HttpdnsUtil isIPv4Address:host]) {
|
||||
return @{ALICLOUDHDNS_IPV4: @[host?:@""]};
|
||||
return @{TrustHDNS_IPV4: @[host?:@""]};
|
||||
} else if ([HttpdnsUtil isIPv6Address:host]) {
|
||||
return @{ALICLOUDHDNS_IPV6: @[host?:@""]};
|
||||
return @{TrustHDNS_IPV6: @[host?:@""]};
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -909,10 +909,10 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
NSArray *ip6s = [hostObject getV6IpStrings];
|
||||
NSMutableDictionary *resultMDic = [NSMutableDictionary dictionary];
|
||||
if ([HttpdnsUtil isNotEmptyArray:ip4s]) {
|
||||
[resultMDic setObject:ip4s forKey:ALICLOUDHDNS_IPV4];
|
||||
[resultMDic setObject:ip4s forKey:TrustHDNS_IPV4];
|
||||
}
|
||||
if ([HttpdnsUtil isNotEmptyArray:ip6s]) {
|
||||
[resultMDic setObject:ip6s forKey:ALICLOUDHDNS_IPV6];
|
||||
[resultMDic setObject:ip6s forKey:TrustHDNS_IPV6];
|
||||
}
|
||||
NSLog(@"getIPv4_v6ByHostAsync result is %@", resultMDic);
|
||||
return resultMDic;
|
||||
@@ -935,9 +935,9 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
if ([HttpdnsUtil isAnIP:host]) {
|
||||
HttpdnsLogDebug("The host is just an IP: %@", host);
|
||||
if ([HttpdnsUtil isIPv4Address:host]) {
|
||||
return @{ALICLOUDHDNS_IPV4: @[host?:@""]};
|
||||
return @{TrustHDNS_IPV4: @[host?:@""]};
|
||||
} else if ([HttpdnsUtil isIPv6Address:host]) {
|
||||
return @{ALICLOUDHDNS_IPV6: @[host?:@""]};
|
||||
return @{TrustHDNS_IPV6: @[host?:@""]};
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -957,10 +957,10 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
NSMutableDictionary *httpdnsResult = [NSMutableDictionary dictionary];
|
||||
NSLog(@"getHttpDnsResultHostAsync result is %@", httpdnsResult);
|
||||
if ([HttpdnsUtil isNotEmptyArray:ip4s]) {
|
||||
[httpdnsResult setObject:ip4s forKey:ALICLOUDHDNS_IPV4];
|
||||
[httpdnsResult setObject:ip4s forKey:TrustHDNS_IPV4];
|
||||
}
|
||||
if ([HttpdnsUtil isNotEmptyArray:ip6s]) {
|
||||
[httpdnsResult setObject:ip6s forKey:ALICLOUDHDNS_IPV6];
|
||||
[httpdnsResult setObject:ip6s forKey:TrustHDNS_IPV6];
|
||||
}
|
||||
return httpdnsResult;
|
||||
}
|
||||
@@ -981,9 +981,9 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
if ([HttpdnsUtil isAnIP:host]) {
|
||||
HttpdnsLogDebug("The host is just an IP: %@", host);
|
||||
if ([HttpdnsUtil isIPv4Address:host]) {
|
||||
return @{ALICLOUDHDNS_IPV4: @[host?:@""]};
|
||||
return @{TrustHDNS_IPV4: @[host?:@""]};
|
||||
} else if ([HttpdnsUtil isIPv6Address:host]) {
|
||||
return @{ALICLOUDHDNS_IPV6: @[host?:@""]};
|
||||
return @{TrustHDNS_IPV6: @[host?:@""]};
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -1005,10 +1005,10 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
NSMutableDictionary *resultMDic = [NSMutableDictionary dictionary];
|
||||
NSLog(@"getIPv4_v6ByHostAsync result is %@", resultMDic);
|
||||
if ([HttpdnsUtil isNotEmptyArray:ip4s]) {
|
||||
[resultMDic setObject:ip4s forKey:ALICLOUDHDNS_IPV4];
|
||||
[resultMDic setObject:ip4s forKey:TrustHDNS_IPV4];
|
||||
}
|
||||
if ([HttpdnsUtil isNotEmptyArray:ip6s]) {
|
||||
[resultMDic setObject:ip6s forKey:ALICLOUDHDNS_IPV6];
|
||||
[resultMDic setObject:ip6s forKey:TrustHDNS_IPV6];
|
||||
}
|
||||
return resultMDic;
|
||||
}
|
||||
@@ -1026,10 +1026,10 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
NSArray *ip6s = [hostObject getV6IpStrings];
|
||||
resultMDic = [NSMutableDictionary dictionary];
|
||||
if ([HttpdnsUtil isNotEmptyArray:ip4s]) {
|
||||
[resultMDic setObject:ip4s forKey:ALICLOUDHDNS_IPV4];
|
||||
[resultMDic setObject:ip4s forKey:TrustHDNS_IPV4];
|
||||
}
|
||||
if ([HttpdnsUtil isNotEmptyArray:ip6s]) {
|
||||
[resultMDic setObject:ip6s forKey:ALICLOUDHDNS_IPV6];
|
||||
[resultMDic setObject:ip6s forKey:TrustHDNS_IPV6];
|
||||
}
|
||||
NSLog(@"###### getHttpDnsResultHostSync result is %@", resultMDic);
|
||||
}
|
||||
@@ -1045,12 +1045,12 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
} else if (stackType == kHttpdnsIpv4Only) {
|
||||
NSArray* ipv4Ips = [self getIpsByHostAsync:host];
|
||||
if (ipv4Ips != nil) {
|
||||
[ipv4_ipv6 setObject:ipv4Ips forKey:ALICLOUDHDNS_IPV4];
|
||||
[ipv4_ipv6 setObject:ipv4Ips forKey:TrustHDNS_IPV4];
|
||||
}
|
||||
} else if (stackType == kHttpdnsIpv6Only) {
|
||||
NSArray* ipv6Ips = [self getIPv6sByHostAsync:host];
|
||||
if (ipv6Ips != nil) {
|
||||
[ipv4_ipv6 setObject:ipv6Ips forKey:ALICLOUDHDNS_IPV6];
|
||||
[ipv4_ipv6 setObject:ipv6Ips forKey:TrustHDNS_IPV6];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1065,12 +1065,12 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
} else if (stackType == kHttpdnsIpv4Only) {
|
||||
NSArray* ipv4IpList = [self getIPv4ListForHostAsync:host];
|
||||
if (ipv4IpList) {
|
||||
[httpdnsResult setObject:ipv4IpList forKey:ALICLOUDHDNS_IPV4];
|
||||
[httpdnsResult setObject:ipv4IpList forKey:TrustHDNS_IPV4];
|
||||
}
|
||||
} else if (stackType == kHttpdnsIpv6Only) {
|
||||
NSArray* ipv6List = [self getIPv6ListForHostAsync:host];
|
||||
if (ipv6List) {
|
||||
[httpdnsResult setObject:ipv6List forKey:ALICLOUDHDNS_IPV6];
|
||||
[httpdnsResult setObject:ipv6List forKey:TrustHDNS_IPV6];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,14 +1083,14 @@ static HttpDnsService *httpdnsSharedStubInstance;
|
||||
if (stackType == kHttpdnsIpv4Only) {
|
||||
NSArray* ipv4IpList = [self getIPv4ListForHostSync:host];
|
||||
if (ipv4IpList) {
|
||||
[httpdnsResult setObject:ipv4IpList forKey:ALICLOUDHDNS_IPV4];
|
||||
[httpdnsResult setObject:ipv4IpList forKey:TrustHDNS_IPV4];
|
||||
}
|
||||
} else if (stackType == kHttpdnsIpDual) {
|
||||
httpdnsResult = [[self getHttpDnsResultHostSync:host] mutableCopy];
|
||||
} else if (stackType == kHttpdnsIpv6Only) {
|
||||
NSArray* ipv6List = [self getIPv6ListForHostSync:host];
|
||||
if (ipv6List) {
|
||||
[httpdnsResult setObject:ipv6List forKey:ALICLOUDHDNS_IPV6];
|
||||
[httpdnsResult setObject:ipv6List forKey:TrustHDNS_IPV6];
|
||||
}
|
||||
}
|
||||
return httpdnsResult;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsIpStackDetector.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2025/3/16.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -11,10 +11,10 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* IP 协议栈类型
|
||||
* IP 协议栈类<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
typedef enum {
|
||||
kHttpdnsIpUnknown = 0, // 未知协议栈
|
||||
kHttpdnsIpUnknown = 0, // 未知协议<EFBFBD><EFBFBD>?
|
||||
kHttpdnsIpv4Only = 1, // IPv4-only
|
||||
kHttpdnsIpv6Only = 2, // IPv6-only
|
||||
kHttpdnsIpDual = 3 // 双栈
|
||||
@@ -23,13 +23,13 @@ typedef enum {
|
||||
@interface HttpdnsIpStackDetector : NSObject
|
||||
|
||||
/**
|
||||
* 返回HttpdnsIpStackDetector的共享实例
|
||||
* 返回HttpdnsIpStackDetector的共享实<EFBFBD><EFBFBD>?
|
||||
* @return HttpdnsIpStackDetector实例
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
* 返回当前缓存的IP协议栈类型,不执行检测
|
||||
* 返回当前缓存的IP协议栈类型,不执行检<EFBFBD><EFBFBD>?
|
||||
* @return HttpdnsIPStackType
|
||||
*/
|
||||
- (HttpdnsIPStackType)currentIpStack;
|
||||
@@ -41,7 +41,7 @@ typedef enum {
|
||||
- (BOOL)isIpv6OnlyNetwork;
|
||||
|
||||
/**
|
||||
* 强制重新检测IP协议栈类型
|
||||
* 强制重新检测IP协议栈类<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (void)redetectIpStack;
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
//
|
||||
// HttpdnsLog_Internal.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by junmo on 2018/12/19.
|
||||
// Copyright © 2018年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2018<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsLog.h"
|
||||
#import "HttpdnsLoggerProtocol.h"
|
||||
#import <pthread/pthread.h>
|
||||
|
||||
// logHandler输出日志,不受日志开关影响
|
||||
// logHandler输出日志,不受日志开关影<EFBFBD><EFBFBD>?
|
||||
#define HttpdnsLogDebug(frmt, ...) \
|
||||
if ([HttpdnsLog validLogHandler]) { \
|
||||
@try { \
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsLoggerProtocol.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by junmo on 2018/12/19.
|
||||
// Copyright © 2018年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2018<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef HttpdnsLoggerProtocol_h
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
@property (nonatomic, setter=setV6TTL:, getter=getV6TTL) int64_t v6ttl;
|
||||
@property (nonatomic, assign) int64_t lastIPv6LookupTime;
|
||||
|
||||
// 用来标记该域名为配置v4记录或v6记录的情况,避免如双栈网络下因为某个协议查不到record需要重复请求
|
||||
// 用来标记该域名为配置v4记录或v6记录的情况,避免如双栈网络下因为某个协议查不到record需要重复请<EFBFBD><EFBFBD>?
|
||||
// 这个信息不用持久化,一次APP启动周期内使用是合适的
|
||||
@property (nonatomic, assign) BOOL hasNoIpv4Record;
|
||||
@property (nonatomic, assign) BOOL hasNoIpv6Record;
|
||||
@@ -71,8 +71,8 @@
|
||||
+ (instancetype)fromDBRecord:(HttpdnsHostRecord *)IPRecord;
|
||||
|
||||
/**
|
||||
* 将当前对象转换为数据库记录对象
|
||||
* @return 数据库记录对象
|
||||
* 将当前对象转换为数据库记录对<EFBFBD><EFBFBD>?
|
||||
* @return 数据库记录对<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (HttpdnsHostRecord *)toDBRecord;
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
/**
|
||||
* 更新指定IP的connectedRT值并重新排序IP列表
|
||||
* @param ip 需要更新的IP地址
|
||||
* @param connectedRT 检测到的RT值,-1表示不可达
|
||||
* @param connectedRT 检测到的RT值,-1表示不可<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (void)updateConnectedRT:(NSInteger)connectedRT forIP:(NSString *)ip;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
// 初始化connectedRT为最大整数值
|
||||
// 初始化connectedRT为最大整数<EFBFBD><EFBFBD>?
|
||||
self.connectedRT = NSIntegerMax;
|
||||
}
|
||||
return self;
|
||||
@@ -129,13 +129,13 @@
|
||||
|
||||
- (BOOL)isIpEmptyUnderQueryIpType:(HttpdnsQueryIPType)queryType {
|
||||
if (queryType & HttpdnsQueryIPTypeIpv4) {
|
||||
// 注意,_hasNoIpv4Record为true时,说明域名没有配置ipv4ip,不是需要去请求的情况
|
||||
// 注意,_hasNoIpv4Record为true时,说明域名没有配置ipv4ip,不是需要去请求的情<EFBFBD><EFBFBD>?
|
||||
if ([HttpdnsUtil isEmptyArray:[self getV4Ips]] && !_hasNoIpv4Record) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
} else if (queryType & HttpdnsQueryIPTypeIpv6 && !_hasNoIpv6Record) {
|
||||
// 注意,_hasNoIpv6Record为true时,说明域名没有配置ipv6ip,不是需要去请求的情况
|
||||
// 注意,_hasNoIpv6Record为true时,说明域名没有配置ipv6ip,不是需要去请求的情<EFBFBD><EFBFBD>?
|
||||
if ([HttpdnsUtil isEmptyArray:[self getV6Ips]] && !_hasNoIpv6Record) {
|
||||
return YES;
|
||||
}
|
||||
@@ -202,14 +202,14 @@
|
||||
}
|
||||
|
||||
- (HttpdnsHostRecord *)toDBRecord {
|
||||
// 将IP对象数组转换为IP字符串数组
|
||||
// 将IP对象数组转换为IP字符串数<EFBFBD><EFBFBD>?
|
||||
NSArray<NSString *> *v4IpStrings = [self getV4IpStrings];
|
||||
NSArray<NSString *> *v6IpStrings = [self getV6IpStrings];
|
||||
|
||||
// 创建当前时间作为modifyAt
|
||||
NSDate *currentDate = [NSDate date];
|
||||
|
||||
// 使用hostName作为cacheKey,保持与fromDBRecord方法的一致性
|
||||
// 使用hostName作为cacheKey,保持与fromDBRecord方法的一致<EFBFBD><EFBFBD>?
|
||||
return [[HttpdnsHostRecord alloc] initWithId:0 // 数据库会自动分配ID
|
||||
cacheKey:self.cacheKey
|
||||
hostName:self.hostName
|
||||
@@ -271,17 +271,17 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据connectedRT值对IP列表进行排序,-1值放在最后
|
||||
// 根据connectedRT值对IP列表进行排序<EFBFBD><EFBFBD>?1值放在最<EFBFBD><EFBFBD>?
|
||||
[mutableIpObjects sortUsingComparator:^NSComparisonResult(HttpdnsIpObject *obj1, HttpdnsIpObject *obj2) {
|
||||
// 如果obj1的connectedRT为-1,将其排在后面
|
||||
// 如果obj1的connectedRT<EFBFBD><EFBFBD>?1,将其排在后<EFBFBD><EFBFBD>?
|
||||
if (obj1.connectedRT == -1) {
|
||||
return NSOrderedDescending;
|
||||
}
|
||||
// 如果obj2的connectedRT为-1,将其排在后面
|
||||
// 如果obj2的connectedRT<EFBFBD><EFBFBD>?1,将其排在后<EFBFBD><EFBFBD>?
|
||||
if (obj2.connectedRT == -1) {
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
// 否则按照connectedRT值从小到大排序
|
||||
// 否则按照connectedRT值从小到大排<EFBFBD><EFBFBD>?
|
||||
return obj1.connectedRT > obj2.connectedRT ? NSOrderedDescending : (obj1.connectedRT < obj2.connectedRT ? NSOrderedAscending : NSOrderedSame);
|
||||
}];
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsHostRecord.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/5/3.
|
||||
// Copyright © 2017年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsHostRecord.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/5/3.
|
||||
// Copyright © 2017年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsHostRecord.h"
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
//
|
||||
// HttpdnsRequest.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/5/19.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#ifndef ALICLOUDHTTPDNSQUERYIPTYPE
|
||||
#define ALICLOUDHTTPDNSQUERYIPTYPE
|
||||
#ifndef TrustHTTPDNSQUERYIPTYPE
|
||||
#define TrustHTTPDNSQUERYIPTYPE
|
||||
|
||||
typedef enum {
|
||||
AlicloudHttpDNS_IPTypeV4 = 0, //ipv4
|
||||
AlicloudHttpDNS_IPTypeV6 = 1, //ipv6
|
||||
AlicloudHttpDNS_IPTypeV64 = 2, //ipv4 + ipv6
|
||||
} AlicloudHttpDNS_IPType;
|
||||
TrustHttpDNS_IPTypeV4 = 0, //ipv4
|
||||
TrustHttpDNS_IPTypeV6 = 1, //ipv6
|
||||
TrustHttpDNS_IPTypeV64 = 2, //ipv4 + ipv6
|
||||
} TrustHttpDNS_IPType;
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, HttpdnsQueryIPType) {
|
||||
HttpdnsQueryIPTypeAuto NS_SWIFT_NAME(auto) = 0,
|
||||
@@ -33,15 +33,15 @@ typedef NS_OPTIONS(NSUInteger, HttpdnsQueryIPType) {
|
||||
/// 需要解析的域名
|
||||
@property (nonatomic, copy) NSString *host;
|
||||
|
||||
/// 解析超时时间,对于同步接口,即为最大等待时间,对于异步接口,即为最大等待回调时间
|
||||
/// 默认值2秒,取值必须在0.5秒 - 5秒之间
|
||||
/// 解析超时时间,对于同步接口,即为最大等待时间,对于异步接口,即为最大等待回调时<EFBFBD><EFBFBD>?
|
||||
/// 默认<EFBFBD><EFBFBD>?秒,取值必须在0.5<EFBFBD><EFBFBD>?- 5秒之<EFBFBD><EFBFBD>?
|
||||
@property (nonatomic, assign) double resolveTimeoutInSecond;
|
||||
|
||||
/// 查询IP类型
|
||||
/// 默认为HttpdnsQueryIPTypeAuto,此类型下,SDK至少会请求解析ipv4地址,若判断到当前网络环境支持ipv6,则还会请求解析ipv6地址
|
||||
/// HttpdnsQueryIPTypeIpv4,只请求解析ipv4
|
||||
/// HttpdnsQueryIPTypeIpv6,只请求解析ipv6
|
||||
/// HttpdnsQueryIPTypeBoth,不管当前网络环境是什么,会尝试同时请求解析ipv4地址和ipv6地址,这种用法,通常需要拿到结果之后自行判断网络环境决定使用哪个结果
|
||||
/// HttpdnsQueryIPTypeBoth,不管当前网络环境是什么,会尝试同时请求解析ipv4地址和ipv6地址,这种用法,通常需要拿到结果之后自行判断网络环境决定使用哪个结<EFBFBD><EFBFBD>?
|
||||
@property (nonatomic, assign) HttpdnsQueryIPType queryIpType;
|
||||
|
||||
/// SDNS参数,针对软件自定义解析场景使用
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsRequest.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/5/19.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsRequest.h"
|
||||
@@ -65,7 +65,7 @@ static double const RESOLVE_HOST_MAX_TIMEOUT_IN_SEC = 5;
|
||||
} else if (_resolveTimeoutInSecond > RESOLVE_HOST_MAX_TIMEOUT_IN_SEC) {
|
||||
_resolveTimeoutInSecond = RESOLVE_HOST_MAX_TIMEOUT_IN_SEC;
|
||||
} else {
|
||||
// 在范围内的正常值
|
||||
// 在范围内的正常<EFBFBD><EFBFBD>?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsRequest_Internal.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/6/19.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef HttpdnsRequest_Internal_h
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsResult.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/5/15.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsResult.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/5/15.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsResult.h"
|
||||
|
||||
@@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HttpdnsNWHTTPClient : NSObject
|
||||
|
||||
/// 全局共享实例,复用底层连接池;线程安全
|
||||
/// 全局共享实例,复用底层连接池;线程安<EFBFBD><EFBFBD>?
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (nullable HttpdnsNWHTTPClientResponse *)performRequestWithURLString:(NSString *)urlString
|
||||
|
||||
@@ -30,7 +30,7 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
@property (nonatomic, strong) dispatch_queue_t poolQueue;
|
||||
|
||||
#if DEBUG
|
||||
// 测试专用统计计数器
|
||||
// 测试专用统计计数<EFBFBD><EFBFBD>?
|
||||
@property (atomic, assign) NSUInteger connectionCreationCount;
|
||||
@property (atomic, assign) NSUInteger connectionReuseCount;
|
||||
#endif
|
||||
@@ -81,7 +81,7 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_poolQueue = dispatch_queue_create("com.alibaba.sdk.httpdns.network.pool", DISPATCH_QUEUE_SERIAL);
|
||||
_poolQueue = dispatch_queue_create("com.Trust.sdk.httpdns.network.pool", DISPATCH_QUEUE_SERIAL);
|
||||
_connectionPool = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
@@ -95,8 +95,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
if (!url) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid resolve URL"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -107,8 +107,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
NSString *host = url.host;
|
||||
if (![HttpdnsUtil isNotEmptyString:host]) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Missing host in resolve URL"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -121,8 +121,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
NSData *requestData = [requestString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (!requestData) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to encode HTTP request"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -136,8 +136,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
error:&connectionError];
|
||||
if (!connection) {
|
||||
if (error) {
|
||||
*error = connectionError ?: [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = connectionError ?: [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Unable to obtain network connection"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -154,8 +154,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
if (!rawResponse) {
|
||||
[self returnConnection:connection forKey:poolKey shouldClose:YES];
|
||||
if (error) {
|
||||
*error = exchangeError ?: [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = exchangeError ?: [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Network request failed"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -168,8 +168,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
if (![self parseHTTPResponseData:rawResponse statusCode:&statusCode headers:&headers body:&bodyData error:&parseError]) {
|
||||
[self returnConnection:connection forKey:poolKey shouldClose:YES];
|
||||
if (error) {
|
||||
*error = parseError ?: [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = parseError ?: [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to parse HTTP response"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -239,8 +239,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
useTLS:useTLS];
|
||||
if (!newConnection) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to create network connection"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -409,8 +409,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
NSString *headerString = [[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding];
|
||||
if (![HttpdnsUtil isNotEmptyString:headerString]) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to decode HTTP headers"}];
|
||||
}
|
||||
return HttpdnsHTTPHeaderParseResultError;
|
||||
@@ -419,8 +419,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
NSArray<NSString *> *lines = [headerString componentsSeparatedByString:@"\r\n"];
|
||||
if (lines.count == 0) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Missing HTTP status line"}];
|
||||
}
|
||||
return HttpdnsHTTPHeaderParseResultError;
|
||||
@@ -437,8 +437,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
|
||||
if (filteredParts.count < 2) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid HTTP status line"}];
|
||||
}
|
||||
return HttpdnsHTTPHeaderParseResultError;
|
||||
@@ -447,8 +447,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
NSInteger localStatus = [filteredParts[1] integerValue];
|
||||
if (localStatus <= 0) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid HTTP status code"}];
|
||||
}
|
||||
return HttpdnsHTTPHeaderParseResultError;
|
||||
@@ -510,8 +510,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
NSString *sizeString = [[NSString alloc] initWithData:sizeData encoding:NSUTF8StringEncoding];
|
||||
if (![HttpdnsUtil isNotEmptyString:sizeString]) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid chunk size"}];
|
||||
}
|
||||
return HttpdnsHTTPChunkParseResultError;
|
||||
@@ -524,8 +524,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
unsigned long long chunkSize = strtoull(cStr, &endPtr, 16);
|
||||
if (endPtr == NULL || endPtr == cStr) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid chunk size"}];
|
||||
}
|
||||
return HttpdnsHTTPChunkParseResultError;
|
||||
@@ -533,8 +533,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
|
||||
if (chunkSize > NSUIntegerMax - cursor) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Chunk size overflow"}];
|
||||
}
|
||||
return HttpdnsHTTPChunkParseResultError;
|
||||
@@ -570,8 +570,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
}
|
||||
if (bytes[cursor] != '\r' || bytes[cursor + 1] != '\n') {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid chunk terminator"}];
|
||||
}
|
||||
return HttpdnsHTTPChunkParseResultError;
|
||||
@@ -589,8 +589,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
error:(NSError **)error {
|
||||
if (!data || data.length == 0) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Empty HTTP response"}];
|
||||
}
|
||||
return NO;
|
||||
@@ -608,8 +608,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
if (headerResult != HttpdnsHTTPHeaderParseResultSuccess) {
|
||||
if (error) {
|
||||
if (headerResult == HttpdnsHTTPHeaderParseResultIncomplete) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Missing HTTP header terminator"}];
|
||||
} else {
|
||||
*error = headerError;
|
||||
@@ -670,8 +670,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
}
|
||||
if (lineEnd + 1 >= length) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid chunked encoding"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -681,8 +681,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
NSString *sizeString = [[NSString alloc] initWithData:sizeData encoding:NSUTF8StringEncoding];
|
||||
if (![HttpdnsUtil isNotEmptyString:sizeString]) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid chunk size"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -695,8 +695,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
// 检查是否是无效的十六进制字符串
|
||||
if (endPtr == cStr) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid chunk size format"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -710,8 +710,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
}
|
||||
if (cursor + chunkSize > length) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Chunk size exceeds buffer"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -720,8 +720,8 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
cursor += chunkSize;
|
||||
if (cursor + 1 >= length || bytes[cursor] != '\r' || bytes[cursor + 1] != '\n') {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid chunk terminator"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -734,7 +734,7 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
|
||||
// 测试专用:通过环境变量跳过 TLS 验证
|
||||
// 仅在设置 HTTPDNS_SKIP_TLS_VERIFY 环境变量时生效(用于本地 mock server 测试)
|
||||
// 仅在设置 HTTPDNS_SKIP_TLS_VERIFY 环境变量时生效(用于本地 mock server 测试<EFBFBD><EFBFBD>?
|
||||
if (getenv("HTTPDNS_SKIP_TLS_VERIFY") != NULL) {
|
||||
return YES;
|
||||
}
|
||||
@@ -777,15 +777,15 @@ static const NSTimeInterval kHttpdnsNWHTTPClientDefaultTimeout = 10.0;
|
||||
if (!userInfo[NSLocalizedDescriptionKey]) {
|
||||
userInfo[NSLocalizedDescriptionKey] = @"Network operation failed";
|
||||
}
|
||||
return [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
return [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:userInfo];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#if DEBUG
|
||||
// 测试专用:连接池检查 API 实现
|
||||
// 测试专用:连接池检<EFBFBD><EFBFBD>?API 实现
|
||||
@implementation HttpdnsNWHTTPClient (TestInspection)
|
||||
|
||||
- (NSUInteger)connectionPoolCountForKey:(NSString *)key {
|
||||
|
||||
@@ -30,7 +30,7 @@ typedef NS_ENUM(NSInteger, HttpdnsHTTPChunkParseResult) {
|
||||
headers:(NSDictionary<NSString *, NSString *> *__autoreleasing _Nullable * _Nullable)headers
|
||||
error:(NSError * _Nullable * _Nullable)error;
|
||||
|
||||
// Chunked 编码检查
|
||||
// Chunked 编码检<EFBFBD><EFBFBD>?
|
||||
- (HttpdnsHTTPChunkParseResult)checkChunkedBodyCompletionInData:(NSData *)data
|
||||
headerEndIndex:(NSUInteger)headerEndIndex
|
||||
error:(NSError * _Nullable * _Nullable)error;
|
||||
@@ -48,7 +48,7 @@ typedef NS_ENUM(NSInteger, HttpdnsHTTPChunkParseResult) {
|
||||
// HTTP 请求构建
|
||||
- (NSString *)buildHTTPRequestStringWithURL:(NSURL *)url userAgent:(NSString *)userAgent;
|
||||
|
||||
// 连接池 key 生成
|
||||
// 连接<EFBFBD><EFBFBD>?key 生成
|
||||
- (NSString *)connectionPoolKeyForHost:(NSString *)host port:(NSString *)port useTLS:(BOOL)useTLS;
|
||||
|
||||
// 错误转换
|
||||
@@ -57,10 +57,10 @@ typedef NS_ENUM(NSInteger, HttpdnsHTTPChunkParseResult) {
|
||||
@end
|
||||
|
||||
#if DEBUG
|
||||
// 测试专用:连接池检查 API
|
||||
// 测试专用:连接池检<EFBFBD><EFBFBD>?API
|
||||
@interface HttpdnsNWHTTPClient (TestInspection)
|
||||
|
||||
// 获取指定 pool key 的连接数量
|
||||
// 获取指定 pool key 的连接数<EFBFBD><EFBFBD>?
|
||||
- (NSUInteger)connectionPoolCountForKey:(NSString *)key;
|
||||
|
||||
// 获取所有连接池 keys
|
||||
@@ -75,7 +75,7 @@ typedef NS_ENUM(NSInteger, HttpdnsHTTPChunkParseResult) {
|
||||
// 连接复用计数(用于验证连接复用)
|
||||
@property (atomic, assign) NSUInteger connectionReuseCount;
|
||||
|
||||
// 重置统计计数器(每个测试开始前调用)
|
||||
// 重置统计计数器(每个测试开始前调用<EFBFBD><EFBFBD>?
|
||||
- (void)resetPoolStatistics;
|
||||
|
||||
@end
|
||||
|
||||
@@ -32,8 +32,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface HttpdnsNWReusableConnection (DebugInspection)
|
||||
|
||||
// 状态检查(这些属性已在主接口暴露,这里仅为文档明确)
|
||||
// @property lastUsedDate - 可读写
|
||||
// @property inUse - 可读写
|
||||
// @property lastUsedDate - 可读<EFBFBD><EFBFBD>?
|
||||
// @property inUse - 可读<EFBFBD><EFBFBD>?
|
||||
// @property invalidated - 只读
|
||||
|
||||
// 测试辅助方法
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
@class HttpdnsNWHTTPClient;
|
||||
|
||||
// 只在此实现文件内可见的交换对象,承载一次请求/响应数据与状态
|
||||
// 只在此实现文件内可见的交换对象,承载一次请<EFBFBD><EFBFBD>?响应数据与状<EFBFBD><EFBFBD>?
|
||||
@interface HttpdnsNWHTTPExchange : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) NSMutableData *buffer;
|
||||
@@ -99,7 +99,7 @@
|
||||
_host = [host copy];
|
||||
_port = [port copy];
|
||||
_useTLS = useTLS;
|
||||
_queue = dispatch_queue_create("com.alibaba.sdk.httpdns.network.connection.reuse", DISPATCH_QUEUE_SERIAL);
|
||||
_queue = dispatch_queue_create("com.Trust.sdk.httpdns.network.connection.reuse", DISPATCH_QUEUE_SERIAL);
|
||||
_stateSemaphore = dispatch_semaphore_create(0);
|
||||
_state = nw_connection_state_invalid;
|
||||
_lastUsedDate = [NSDate date];
|
||||
@@ -135,14 +135,14 @@
|
||||
if (secTrust && strongSelf) {
|
||||
SecTrustRef trustRef = sec_trust_copy_ref(secTrust);
|
||||
if (trustRef) {
|
||||
NSString *validIP = ALICLOUD_HTTPDNS_VALID_SERVER_CERTIFICATE_IP;
|
||||
NSString *validIP = Trust_HTTPDNS_VALID_SERVER_CERTIFICATE_IP;
|
||||
isValid = [strongSelf.client evaluateServerTrust:trustRef forDomain:validIP];
|
||||
if (!isValid && [HttpdnsUtil isNotEmptyString:strongSelf.host]) {
|
||||
isValid = [strongSelf.client evaluateServerTrust:trustRef forDomain:strongSelf.host];
|
||||
}
|
||||
if (!isValid && !strongSelf.stateError) {
|
||||
strongSelf.stateError = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
strongSelf.stateError = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"TLS trust validation failed"}];
|
||||
}
|
||||
CFRelease(trustRef);
|
||||
@@ -220,8 +220,8 @@
|
||||
- (BOOL)openWithTimeout:(NSTimeInterval)timeout error:(NSError **)error {
|
||||
if (self.invalidated) {
|
||||
if (error) {
|
||||
*error = _stateError ?: [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = _stateError ?: [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Connection invalid"}];
|
||||
}
|
||||
return NO;
|
||||
@@ -237,8 +237,8 @@
|
||||
if (waitResult != 0) {
|
||||
self.invalidated = YES;
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Connection setup timed out"}];
|
||||
}
|
||||
nw_connection_cancel(_connectionHandle);
|
||||
@@ -250,8 +250,8 @@
|
||||
}
|
||||
|
||||
if (error) {
|
||||
*error = _stateError ?: [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = _stateError ?: [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Connection failed to become ready"}];
|
||||
}
|
||||
return NO;
|
||||
@@ -277,8 +277,8 @@
|
||||
error:(NSError **)error {
|
||||
if (!requestData || requestData.length == 0) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Empty HTTP request"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -286,8 +286,8 @@
|
||||
|
||||
if (![self isViable] || self.currentExchange) {
|
||||
if (error) {
|
||||
*error = _stateError ?: [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
*error = _stateError ?: [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Connection not ready"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -299,16 +299,16 @@
|
||||
dispatch_sync(_queue, ^{
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
exchange.error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
exchange.error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Connection released unexpectedly"}];
|
||||
exchange.finished = YES;
|
||||
dispatch_semaphore_signal(exchange.semaphore);
|
||||
return;
|
||||
}
|
||||
if (strongSelf.invalidated || strongSelf.currentExchange) {
|
||||
exchange.error = strongSelf.stateError ?: [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
exchange.error = strongSelf.stateError ?: [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Connection is busy"}];
|
||||
exchange.finished = YES;
|
||||
dispatch_semaphore_signal(exchange.semaphore);
|
||||
@@ -320,8 +320,8 @@
|
||||
if (exchange.finished) {
|
||||
return;
|
||||
}
|
||||
exchange.error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
exchange.error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Request timed out"}];
|
||||
exchange.finished = YES;
|
||||
dispatch_semaphore_signal(exchange.semaphore);
|
||||
@@ -366,8 +366,8 @@
|
||||
|
||||
if (waitResult != 0) {
|
||||
if (!exchange.error) {
|
||||
exchange.error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
exchange.error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Request wait timed out"}];
|
||||
}
|
||||
[self invalidate];
|
||||
@@ -428,8 +428,8 @@
|
||||
if (is_complete) {
|
||||
exchange.remoteClosed = YES;
|
||||
if (!exchange.finished) {
|
||||
exchange.error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
exchange.error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_COMMON_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Connection closed before response completed"}];
|
||||
exchange.finished = YES;
|
||||
dispatch_semaphore_signal(exchange.semaphore);
|
||||
@@ -452,7 +452,7 @@
|
||||
}
|
||||
|
||||
if (isComplete) {
|
||||
// 远端已经发送完并关闭,需要提前标记,避免提前返回时漏记连接状态
|
||||
// 远端已经发送完并关闭,需要提前标记,避免提前返回时漏记连接状<EFBFBD><EFBFBD>?
|
||||
exchange.remoteClosed = YES;
|
||||
}
|
||||
|
||||
@@ -527,7 +527,7 @@
|
||||
@end
|
||||
|
||||
#if DEBUG
|
||||
// 测试专用:连接状态操作实现
|
||||
// 测试专用:连接状态操作实<EFBFBD><EFBFBD>?
|
||||
@implementation HttpdnsNWReusableConnection (DebugInspection)
|
||||
|
||||
- (void)debugSetLastUsedDate:(nullable NSDate *)date {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsDB.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2025/3/15.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -19,53 +19,53 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* 初始化数据库
|
||||
* @param accountId 账户ID
|
||||
* @return 数据库实例
|
||||
* @return 数据库实<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (instancetype)initWithAccountId:(NSInteger)accountId;
|
||||
|
||||
/**
|
||||
* 创建或更新记录
|
||||
* 创建或更新记<EFBFBD><EFBFBD>?
|
||||
* @param record 主机记录
|
||||
* @return 是否成功
|
||||
*/
|
||||
- (BOOL)createOrUpdate:(HttpdnsHostRecord *)record;
|
||||
|
||||
/**
|
||||
* 根据缓存键查询记录
|
||||
* @param cacheKey 缓存键
|
||||
* 根据缓存键查询记<EFBFBD><EFBFBD>?
|
||||
* @param cacheKey 缓存<EFBFBD><EFBFBD>?
|
||||
* @return 查询到的记录,如果不存在则返回nil
|
||||
*/
|
||||
- (nullable HttpdnsHostRecord *)selectByCacheKey:(NSString *)cacheKey;
|
||||
|
||||
/**
|
||||
* 根据缓存键删除记录
|
||||
* @param cacheKey 缓存键
|
||||
* 根据缓存键删除记<EFBFBD><EFBFBD>?
|
||||
* @param cacheKey 缓存<EFBFBD><EFBFBD>?
|
||||
* @return 是否成功
|
||||
*/
|
||||
- (BOOL)deleteByCacheKey:(NSString *)cacheKey;
|
||||
|
||||
/**
|
||||
* 根据主机名数组批量删除记录
|
||||
* @param hostNameArr 主机名数组
|
||||
* @return 成功删除的记录数量
|
||||
* 根据主机名数组批量删除记<EFBFBD><EFBFBD>?
|
||||
* @param hostNameArr 主机名数<EFBFBD><EFBFBD>?
|
||||
* @return 成功删除的记录数<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (NSInteger)deleteByHostNameArr:(NSArray<NSString *> *)hostNameArr;
|
||||
|
||||
/**
|
||||
* 获取所有缓存记录
|
||||
* @return 所有缓存记录数组
|
||||
* 获取所有缓存记<EFBFBD><EFBFBD>?
|
||||
* @return 所有缓存记录数<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (NSArray<HttpdnsHostRecord *> *)getAllRecords;
|
||||
|
||||
/**
|
||||
* 清理指定时间点已过期的记录
|
||||
* @param specifiedTime 指定的时间点(epoch时间)
|
||||
* @return 清理的记录数量
|
||||
* 清理指定时间点已过期的记<EFBFBD><EFBFBD>?
|
||||
* @param specifiedTime 指定的时间点(epoch时间<EFBFBD><EFBFBD>?
|
||||
* @return 清理的记录数<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (NSInteger)cleanRecordAlreadExpiredAt:(NSTimeInterval)specifiedTime;
|
||||
|
||||
/**
|
||||
* 删除所有记录
|
||||
* 删除所有记<EFBFBD><EFBFBD>?
|
||||
* @return 是否成功
|
||||
*/
|
||||
- (BOOL)deleteAll;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
+ (NSString *)httpdnsDataDirectory;
|
||||
+ (NSString *)scheduleCenterResultDirectory;
|
||||
/// 多账号隔离:返回指定账号的调度结果目录
|
||||
/// 多账号隔离:返回指定账号的调度结果目<EFBFBD><EFBFBD>?
|
||||
+ (NSString *)scheduleCenterResultDirectoryForAccount:(NSInteger)accountId;
|
||||
|
||||
+ (BOOL)saveJSON:(id)JSON toPath:(NSString *)path;
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#import "HttpdnsService.h"
|
||||
#import "HttpdnsUtil.h"
|
||||
|
||||
static NSString *const ALICLOUD_HTTPDNS_ROOT_DIR_NAME = @"HTTPDNS";
|
||||
static NSString *const ALICLOUD_HTTPDNS_HOST_CACHE_DIR_NAME = @"HostCache";
|
||||
static NSString *const Trust_HTTPDNS_ROOT_DIR_NAME = @"HTTPDNS";
|
||||
static NSString *const Trust_HTTPDNS_HOST_CACHE_DIR_NAME = @"HostCache";
|
||||
|
||||
static dispatch_queue_t _fileCacheQueue = 0;
|
||||
|
||||
@@ -33,7 +33,7 @@ static dispatch_queue_t _fileCacheQueue = 0;
|
||||
+ (void)initialize {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_fileCacheQueue = dispatch_queue_create("com.alibaba.sdk.httpdns.fileCacheQueue", DISPATCH_QUEUE_SERIAL);
|
||||
_fileCacheQueue = dispatch_queue_create("com.Trust.sdk.httpdns.fileCacheQueue", DISPATCH_QUEUE_SERIAL);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
|
||||
@interface HttpdnsScheduleCenter : NSObject
|
||||
|
||||
/// 针对多账号场景的调度中心构造方法
|
||||
/// 注意:若无需多账号隔离,可继续使用 sharedInstance
|
||||
/// 针对多账号场景的调度中心构造方<EFBFBD><EFBFBD>?
|
||||
/// 注意:若无需多账号隔离,可继续使<EFBFBD><EFBFBD>?sharedInstance
|
||||
- (instancetype)initWithAccountId:(NSInteger)accountId;
|
||||
|
||||
- (void)initRegion:(NSString *)region;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsScheduleExecutor.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/4/11.
|
||||
// Copyright © 2017年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsScheduleExecutor.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/4/11.
|
||||
// Copyright © 2017年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsScheduleExecutor.h"
|
||||
@@ -94,8 +94,8 @@
|
||||
if (response.statusCode != 200) {
|
||||
NSDictionary *dict = @{@"ResponseCode": [NSString stringWithFormat:@"%ld", (long)response.statusCode]};
|
||||
if (pError) {
|
||||
*pError = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_HTTPS_NO_DATA_ERROR_CODE
|
||||
*pError = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_HTTPS_NO_DATA_ERROR_CODE
|
||||
userInfo:dict];
|
||||
}
|
||||
return nil;
|
||||
@@ -118,8 +118,8 @@
|
||||
}
|
||||
|
||||
if (pError) {
|
||||
*pError = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTP_PARSE_JSON_FAILED
|
||||
*pError = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTP_PARSE_JSON_FAILED
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to parse JSON response"}];
|
||||
}
|
||||
if (pError != NULL) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpDnsLocker.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by 王贇 on 2023/8/16.
|
||||
// Copyright © 2023 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2023 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef HttpDnsLocker_h
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpDnsLocker.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by 王贇 on 2023/8/16.
|
||||
// Copyright © 2023 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2023 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpDnsLocker.h"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsHostObjectInMemoryCache.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/9/28.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -11,10 +11,10 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// 这个字典在HTTPDNS中只用于存储HttpdnsHostObject对象,这个对象是整个框架的核心对象,用于缓存和处理域名解析结果
|
||||
// 通常从缓存中获得这个对象之后,会根据不同场景改变一些字段的值,而且很可能发生在不同线程中
|
||||
// 这个字典在HTTPDNS中只用于存储HttpdnsHostObject对象,这个对象是整个框架的核心对象,用于缓存和处理域名解析结<EFBFBD><EFBFBD>?
|
||||
// 通常从缓存中获得这个对象之后,会根据不同场景改变一些字段的值,而且很可能发生在不同线程<EFBFBD><EFBFBD>?
|
||||
// 而不同线程从缓存中直接读取共享对象的话,很有可能发生线程竞争的情况,多线程访问某个对象的同一个字段,在swift环境有较高概率发生crash
|
||||
// 因此,除了确保字典操作的线程安全,拿出对象的时候,也直接copy一个复制对象返回(HttpdnsHostObject对象实现了NSCopying协议)
|
||||
// 因此,除了确保字典操作的线程安全,拿出对象的时候,也直接copy一个复制对象返<EFBFBD><EFBFBD>?HttpdnsHostObject对象实现了NSCopying协议)
|
||||
@interface HttpdnsHostObjectInMemoryCache : NSObject
|
||||
|
||||
- (void)setHostObject:(HttpdnsHostObject *)object forCacheKey:(NSString *)key;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsHostObjectInMemoryCache.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/9/28.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsHostObjectInMemoryCache.h"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsIPQualityDetector.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2025/3/13.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -11,10 +11,10 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* IP质量检测回调
|
||||
* @param cacheKey 缓存键
|
||||
* IP质量检测回<EFBFBD><EFBFBD>?
|
||||
* @param cacheKey 缓存<EFBFBD><EFBFBD>?
|
||||
* @param ip IP地址
|
||||
* @param costTime 连接耗时(毫秒),-1表示连接失败
|
||||
* @param costTime 连接耗时(毫秒)<EFBFBD><EFBFBD>?1表示连接失败
|
||||
*/
|
||||
typedef void(^HttpdnsIPQualityCallback)(NSString *cacheKey, NSString *ip, NSInteger costTime);
|
||||
|
||||
@@ -32,10 +32,10 @@ typedef void(^HttpdnsIPQualityCallback)(NSString *cacheKey, NSString *ip, NSInte
|
||||
|
||||
/**
|
||||
* 调度一个IP连接质量检测任务,不会阻塞当前线程
|
||||
* @param cacheKey 缓存键,通常是域名
|
||||
* @param cacheKey 缓存键,通常是域<EFBFBD><EFBFBD>?
|
||||
* @param ip 要检测的IP地址
|
||||
* @param port 连接端口,如果为nil则默认使用80
|
||||
* @param callback 检测完成后的回调
|
||||
* @param port 连接端口,如果为nil则默认使<EFBFBD><EFBFBD>?0
|
||||
* @param callback 检测完成后的回<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (void)scheduleIPQualityDetection:(NSString *)cacheKey
|
||||
ip:(NSString *)ip
|
||||
@@ -45,12 +45,12 @@ typedef void(^HttpdnsIPQualityCallback)(NSString *cacheKey, NSString *ip, NSInte
|
||||
#pragma mark - Methods exposed for testing
|
||||
|
||||
/**
|
||||
* 执行IP连接质量检测
|
||||
* @param cacheKey 缓存键,通常是域名
|
||||
* 执行IP连接质量检<EFBFBD><EFBFBD>?
|
||||
* @param cacheKey 缓存键,通常是域<EFBFBD><EFBFBD>?
|
||||
* @param ip 要检测的IP地址
|
||||
* @param port 连接端口,如果为nil则默认使用80
|
||||
* @param callback 检测完成后的回调
|
||||
* @note 此方法主要用于测试
|
||||
* @param port 连接端口,如果为nil则默认使<EFBFBD><EFBFBD>?0
|
||||
* @param callback 检测完成后的回<EFBFBD><EFBFBD>?
|
||||
* @note 此方法主要用于测<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (void)executeDetection:(NSString *)cacheKey
|
||||
ip:(NSString *)ip
|
||||
@@ -58,21 +58,21 @@ typedef void(^HttpdnsIPQualityCallback)(NSString *cacheKey, NSString *ip, NSInte
|
||||
callback:(HttpdnsIPQualityCallback)callback;
|
||||
|
||||
/**
|
||||
* 建立TCP连接并测量连接时间
|
||||
* 建立TCP连接并测量连接时<EFBFBD><EFBFBD>?
|
||||
* @param ip 要连接的IP地址
|
||||
* @param port 连接端口
|
||||
* @return 连接耗时(毫秒),-1表示连接失败
|
||||
* @note 此方法主要用于测试
|
||||
* @return 连接耗时(毫秒)<EFBFBD><EFBFBD>?1表示连接失败
|
||||
* @note 此方法主要用于测<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (NSInteger)tcpConnectToIP:(NSString *)ip port:(int)port;
|
||||
|
||||
/**
|
||||
* 添加待处理任务
|
||||
* @param cacheKey 缓存键,通常是域名
|
||||
* 添加待处理任<EFBFBD><EFBFBD>?
|
||||
* @param cacheKey 缓存键,通常是域<EFBFBD><EFBFBD>?
|
||||
* @param ip 要检测的IP地址
|
||||
* @param port 连接端口
|
||||
* @param callback 检测完成后的回调
|
||||
* @note 此方法主要用于测试
|
||||
* @param callback 检测完成后的回<EFBFBD><EFBFBD>?
|
||||
* @note 此方法主要用于测<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (void)addPendingTask:(NSString *)cacheKey
|
||||
ip:(NSString *)ip
|
||||
@@ -80,14 +80,14 @@ typedef void(^HttpdnsIPQualityCallback)(NSString *cacheKey, NSString *ip, NSInte
|
||||
callback:(HttpdnsIPQualityCallback)callback;
|
||||
|
||||
/**
|
||||
* 处理待处理任务队列
|
||||
* @note 此方法主要用于测试
|
||||
* 处理待处理任务队<EFBFBD><EFBFBD>?
|
||||
* @note 此方法主要用于测<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (void)processPendingTasksIfNeeded;
|
||||
|
||||
/**
|
||||
* 处理所有待处理任务
|
||||
* @note 此方法主要用于测试
|
||||
* @note 此方法主要用于测<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
- (void)processPendingTasks;
|
||||
|
||||
|
||||
@@ -463,7 +463,7 @@ static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRea
|
||||
return HttpdnsReachableVia5G;
|
||||
}
|
||||
}
|
||||
// 默认以使用最广泛的4G兜底
|
||||
// 默认以使用最广泛<EFBFBD><EFBFBD>?G兜底
|
||||
return HttpdnsReachableVia4G;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -222,8 +222,8 @@
|
||||
|
||||
/**
|
||||
生成sessionId
|
||||
App打开生命周期只生成一次,不做持久化
|
||||
sessionId为12位,采用base62编码
|
||||
App打开生命周期只生成一次,不做持久<EFBFBD><EFBFBD>?
|
||||
sessionId<EFBFBD><EFBFBD>?2位,采用base62编码
|
||||
*/
|
||||
+ (NSString *)generateSessionID {
|
||||
static NSString *sessionId = nil;
|
||||
@@ -256,11 +256,11 @@
|
||||
+ (NSData *)encryptDataAESCBC:(NSData *)plaintext
|
||||
withKey:(NSData *)key
|
||||
error:(NSError **)error {
|
||||
// 检查输入参数
|
||||
// 检查输入参<EFBFBD><EFBFBD>?
|
||||
if (plaintext == nil || [plaintext length] == 0 || key == nil || [key length] != kCCKeySizeAES128) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_INVALID_PARAMS_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_ENCRYPT_INVALID_PARAMS_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid input parameters"}];
|
||||
}
|
||||
return nil;
|
||||
@@ -271,18 +271,18 @@
|
||||
int result = SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, iv.mutableBytes);
|
||||
if (result != 0) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_RANDOM_IV_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_ENCRYPT_RANDOM_IV_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Failed to generate random IV"}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 计算加密后的数据长度 (可能需要填充)
|
||||
// 计算加密后的数据长度 (可能需要填<EFBFBD><EFBFBD>?
|
||||
size_t bufferSize = [plaintext length] + kCCBlockSizeAES128;
|
||||
size_t encryptedSize = 0;
|
||||
|
||||
// 创建输出缓冲区
|
||||
// 创建输出缓冲<EFBFBD><EFBFBD>?
|
||||
NSMutableData *cipherData = [NSMutableData dataWithLength:bufferSize];
|
||||
|
||||
// 执行加密
|
||||
@@ -301,17 +301,17 @@
|
||||
|
||||
if (cryptStatus != kCCSuccess) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_FAILED_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_ENCRYPT_FAILED_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Encryption failed with status: %d", cryptStatus]}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 调整加密数据的长度,只保留实际加密内容
|
||||
// 调整加密数据的长度,只保留实际加密内<EFBFBD><EFBFBD>?
|
||||
[cipherData setLength:encryptedSize];
|
||||
|
||||
// 将IV和加密数据合并在一起
|
||||
// 将IV和加密数据合并在一<EFBFBD><EFBFBD>?
|
||||
NSMutableData *resultData = [NSMutableData dataWithData:iv];
|
||||
[resultData appendData:cipherData];
|
||||
|
||||
@@ -338,7 +338,7 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 移除可能存在的空格
|
||||
// 移除可能存在的空<EFBFBD><EFBFBD>?
|
||||
NSString *cleanedString = [hexString stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
|
||||
// 确保字符串长度为偶数
|
||||
@@ -391,25 +391,25 @@
|
||||
+ (NSData *)decryptDataAESCBC:(NSData *)ciphertext
|
||||
withKey:(NSData *)key
|
||||
error:(NSError **)error {
|
||||
// 检查输入参数
|
||||
// 检查输入参<EFBFBD><EFBFBD>?
|
||||
if (ciphertext == nil || [ciphertext length] <= kCCBlockSizeAES128 || key == nil || [key length] != kCCKeySizeAES128) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_INVALID_PARAMS_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_ENCRYPT_INVALID_PARAMS_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid input parameters for decryption"}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 提取IV(前16字节)和实际的密文
|
||||
// 提取IV(前16字节)和实际的密<EFBFBD><EFBFBD>?
|
||||
NSData *iv = [ciphertext subdataWithRange:NSMakeRange(0, kCCBlockSizeAES128)];
|
||||
NSData *actualCiphertext = [ciphertext subdataWithRange:NSMakeRange(kCCBlockSizeAES128, ciphertext.length - kCCBlockSizeAES128)];
|
||||
|
||||
// 计算解密后可能的缓冲区大小
|
||||
// 计算解密后可能的缓冲区大<EFBFBD><EFBFBD>?
|
||||
size_t bufferSize = actualCiphertext.length + kCCBlockSizeAES128;
|
||||
size_t decryptedSize = 0;
|
||||
|
||||
// 创建输出缓冲区
|
||||
// 创建输出缓冲<EFBFBD><EFBFBD>?
|
||||
NSMutableData *decryptedData = [NSMutableData dataWithLength:bufferSize];
|
||||
|
||||
// 执行解密
|
||||
@@ -427,14 +427,14 @@
|
||||
|
||||
if (cryptStatus != kCCSuccess) {
|
||||
if (error) {
|
||||
*error = [NSError errorWithDomain:ALICLOUD_HTTPDNS_ERROR_DOMAIN
|
||||
code:ALICLOUD_HTTPDNS_ENCRYPT_FAILED_ERROR_CODE
|
||||
*error = [NSError errorWithDomain:Trust_HTTPDNS_ERROR_DOMAIN
|
||||
code:Trust_HTTPDNS_ENCRYPT_FAILED_ERROR_CODE
|
||||
userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Decryption failed with status: %d", cryptStatus]}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 调整解密数据的长度,只保留实际解密内容
|
||||
// 调整解密数据的长度,只保留实际解密内<EFBFBD><EFBFBD>?
|
||||
[decryptedData setLength:decryptedSize];
|
||||
|
||||
return decryptedData;
|
||||
@@ -453,14 +453,14 @@
|
||||
|
||||
if (dnsService.ttlDelegate && [dnsService.ttlDelegate respondsToSelector:@selector(httpdnsHost:ipType:ttl:)]) {
|
||||
if ([self isNotEmptyArray:[hostObject getV4Ips]]) {
|
||||
int64_t customV4TTL = [dnsService.ttlDelegate httpdnsHost:host ipType:AlicloudHttpDNS_IPTypeV4 ttl:hostObject.v4ttl];
|
||||
int64_t customV4TTL = [dnsService.ttlDelegate httpdnsHost:host ipType:TrustHttpDNS_IPTypeV4 ttl:hostObject.v4ttl];
|
||||
if (customV4TTL > 0) {
|
||||
hostObject.v4ttl = customV4TTL;
|
||||
}
|
||||
}
|
||||
|
||||
if ([self isNotEmptyArray:[hostObject getV6Ips]]) {
|
||||
int64_t customV6TTL = [dnsService.ttlDelegate httpdnsHost:host ipType:AlicloudHttpDNS_IPTypeV6 ttl:hostObject.v6ttl];
|
||||
int64_t customV6TTL = [dnsService.ttlDelegate httpdnsHost:host ipType:TrustHttpDNS_IPTypeV6 ttl:hostObject.v6ttl];
|
||||
if (customV6TTL > 0) {
|
||||
hostObject.v6ttl = customV6TTL;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// AppDelegate.h
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// Created by junmo on 2018/8/3.
|
||||
// Copyright © 2018年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2018<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// AppDelegate.m
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// Created by junmo on 2018/8/3.
|
||||
// Copyright © 2018年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2018<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// DemoConfigLoader.h
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// DemoConfigLoader.m
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
@@ -30,7 +30,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
// 复杂逻辑:配置加载顺序为 Bundle > 环境变量;并对 accountID 进行有效性校验
|
||||
// 复杂逻辑:配置加载顺序为 Bundle > 环境变量;并<EFBFBD><EFBFBD>?accountID 进行有效性校<EFBFBD><EFBFBD>?
|
||||
- (void)loadConfig {
|
||||
_accountID = 0;
|
||||
_secretKey = @"";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// DemoHttpdnsScenario.h
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-23
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// DemoLogViewController.h
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// DemoLogViewController.m
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// DemoResolveModel.h
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//
|
||||
// DNSDemoViewController.h
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// main.m
|
||||
// AlicloudHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// Created by junmo on 2018/8/3.
|
||||
// Copyright © 2018年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2018<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// DBTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2025/3/15.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// CacheKeyFunctionTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/6/12.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -70,7 +70,7 @@ static NSString *sdnsHost = @"sdns1.onlyforhttpdnstest.run.place";
|
||||
// 清空缓存
|
||||
[self.httpdns.requestManager cleanAllHostMemoryCache];
|
||||
|
||||
// 从db再加载到缓存中
|
||||
// 从db再加载到缓存<EFBFBD><EFBFBD>?
|
||||
[self.httpdns.requestManager syncLoadCacheFromDbToMemory];
|
||||
|
||||
HttpdnsResult *result = [self.httpdns resolveHostSyncNonBlocking:testHost byIpType:HttpdnsQueryIPTypeIpv4];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// CustomTTLAndCleanCacheTest.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/6/17.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -38,8 +38,8 @@ static int TEST_CUSTOM_TTL_SECOND = 3;
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (int64_t)httpdnsHost:(NSString *)host ipType:(AlicloudHttpDNS_IPType)ipType ttl:(int64_t)ttl {
|
||||
// 为了在并发测试中域名快速过期,将ttl设置为3秒
|
||||
- (int64_t)httpdnsHost:(NSString *)host ipType:(TrustHttpDNS_IPType)ipType ttl:(int64_t)ttl {
|
||||
// 为了在并发测试中域名快速过期,将ttl设置<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?
|
||||
NSString *testHost = hostNameIpPrefixMap.allKeys.firstObject;
|
||||
if ([host isEqual:testHost]) {
|
||||
return TEST_CUSTOM_TTL_SECOND;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// EnableReuseExpiredIpTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/5/28.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -39,8 +39,8 @@ static int ttlForTest = 3;
|
||||
self.currentTimeStamp = [[NSDate date] timeIntervalSince1970];
|
||||
}
|
||||
|
||||
- (int64_t)httpdnsHost:(NSString *)host ipType:(AlicloudHttpDNS_IPType)ipType ttl:(int64_t)ttl {
|
||||
// 在测试中域名快速过期
|
||||
- (int64_t)httpdnsHost:(NSString *)host ipType:(TrustHttpDNS_IPType)ipType ttl:(int64_t)ttl {
|
||||
// 在测试中域名快速过<EFBFBD><EFBFBD>?
|
||||
return ttlForTest;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ static int ttlForTest = 3;
|
||||
XCTAssertTrue([result2.host isEqualToString:host]);
|
||||
XCTAssertGreaterThan(result2.ttl, 0);
|
||||
XCTAssertLessThanOrEqual(result2.ttl, ttlForTest);
|
||||
// 因为运行复用过期解析结果,因此这里获得的一定是已经过期的
|
||||
// 因为运行复用过期解析结果,因此这里获得的一定是已经过期<EFBFBD><EFBFBD>?
|
||||
XCTAssertGreaterThan([[NSDate date] timeIntervalSince1970], result2.lastUpdatedTimeInterval + result2.ttl);
|
||||
NSString *firstIp2 = [result2 firstIpv4Address];
|
||||
XCTAssertTrue([firstIp2 hasPrefix:ipPrefix]);
|
||||
@@ -81,13 +81,13 @@ static int ttlForTest = 3;
|
||||
// 等待第二次解析触发的请求完成
|
||||
[NSThread sleepForTimeInterval:1];
|
||||
|
||||
// 再次使用nonblocking方法解析,此时应该已经拿到有效结果
|
||||
// 再次使用nonblocking方法解析,此时应该已经拿到有效结<EFBFBD><EFBFBD>?
|
||||
HttpdnsResult *result3 = [self.httpdns resolveHostSyncNonBlocking:host byIpType:HttpdnsQueryIPTypeIpv4];
|
||||
XCTAssertNotNil(result3);
|
||||
XCTAssertTrue([result3.host isEqualToString:host]);
|
||||
XCTAssertGreaterThan(result3.ttl, 0);
|
||||
XCTAssertLessThanOrEqual(result3.ttl, ttlForTest);
|
||||
// 有效结果必定未过期
|
||||
// 有效结果必定未过<EFBFBD><EFBFBD>?
|
||||
XCTAssertLessThan([[NSDate date] timeIntervalSince1970], result3.lastUpdatedTimeInterval + result3.ttl);
|
||||
NSString *firstIp3 = [result3 firstIpv4Address];
|
||||
XCTAssertTrue([firstIp3 hasPrefix:ipPrefix]);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsHostObjectTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2025/3/14.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -26,19 +26,19 @@
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
#pragma mark - 基本属性测试
|
||||
#pragma mark - 基本属性测<EFBFBD><EFBFBD>?
|
||||
|
||||
- (void)testHostObjectProperties {
|
||||
// 创建一个HttpdnsHostObject实例
|
||||
HttpdnsHostObject *hostObject = [[HttpdnsHostObject alloc] init];
|
||||
|
||||
// 设置基本属性
|
||||
// 设置基本属<EFBFBD><EFBFBD>?
|
||||
hostObject.host = @"example.com";
|
||||
hostObject.ttl = 60;
|
||||
hostObject.queryTimes = 1;
|
||||
hostObject.clientIP = @"192.168.1.1";
|
||||
|
||||
// 验证属性值
|
||||
// 验证属性<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqualObjects(hostObject.host, @"example.com", @"host属性应该被正确设置");
|
||||
XCTAssertEqual(hostObject.ttl, 60, @"ttl属性应该被正确设置");
|
||||
XCTAssertEqual(hostObject.queryTimes, 1, @"queryTimes属性应该被正确设置");
|
||||
@@ -51,13 +51,13 @@
|
||||
// 创建一个HttpdnsIpObject实例
|
||||
HttpdnsIpObject *ipObject = [[HttpdnsIpObject alloc] init];
|
||||
|
||||
// 设置基本属性
|
||||
// 设置基本属<EFBFBD><EFBFBD>?
|
||||
ipObject.ip = @"1.2.3.4";
|
||||
ipObject.ttl = 300;
|
||||
ipObject.priority = 10;
|
||||
ipObject.detectRT = 50; // 测试新添加的detectRT属性
|
||||
ipObject.detectRT = 50; // 测试新添加的detectRT属<EFBFBD><EFBFBD>?
|
||||
|
||||
// 验证属性值
|
||||
// 验证属性<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqualObjects(ipObject.ip, @"1.2.3.4", @"ip属性应该被正确设置");
|
||||
XCTAssertEqual(ipObject.ttl, 300, @"ttl属性应该被正确设置");
|
||||
XCTAssertEqual(ipObject.priority, 10, @"priority属性应该被正确设置");
|
||||
@@ -68,18 +68,18 @@
|
||||
// 创建一个HttpdnsIpObject实例
|
||||
HttpdnsIpObject *ipObject = [[HttpdnsIpObject alloc] init];
|
||||
|
||||
// 测试默认值
|
||||
// 测试默认<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(ipObject.detectRT, -1, @"detectRT的默认值应该是-1");
|
||||
|
||||
// 测试设置检测时间
|
||||
// 测试设置检测时<EFBFBD><EFBFBD>?
|
||||
[ipObject setDetectRT:100];
|
||||
XCTAssertEqual(ipObject.detectRT, 100, @"detectRT应该被正确设置为100");
|
||||
|
||||
// 测试设置为负值
|
||||
// 测试设置为负<EFBFBD><EFBFBD>?
|
||||
[ipObject setDetectRT:-5];
|
||||
XCTAssertEqual(ipObject.detectRT, -1, @"设置负值时detectRT应该被设置为-1");
|
||||
|
||||
// 测试设置为0
|
||||
// 测试设置<EFBFBD><EFBFBD>?
|
||||
[ipObject setDetectRT:0];
|
||||
XCTAssertEqual(ipObject.detectRT, 0, @"detectRT应该被正确设置为0");
|
||||
}
|
||||
@@ -102,15 +102,15 @@
|
||||
ipv6Object.ttl = 600;
|
||||
ipv6Object.detectRT = 80;
|
||||
|
||||
// 添加IP对象到主机对象
|
||||
// 添加IP对象到主机对<EFBFBD><EFBFBD>?
|
||||
[hostObject addIpv4:ipv4Object];
|
||||
[hostObject addIpv6:ipv6Object];
|
||||
|
||||
// 验证IP对象是否被正确添加
|
||||
XCTAssertEqual(hostObject.ipv4List.count, 1, @"应该有1个IPv4对象");
|
||||
XCTAssertEqual(hostObject.ipv6List.count, 1, @"应该有1个IPv6对象");
|
||||
// 验证IP对象是否被正确添<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(hostObject.ipv4List.count, 1, @"应该<EFBFBD><EFBFBD>?个IPv4对象");
|
||||
XCTAssertEqual(hostObject.ipv6List.count, 1, @"应该<EFBFBD><EFBFBD>?个IPv6对象");
|
||||
|
||||
// 验证IP对象的属性
|
||||
// 验证IP对象的属<EFBFBD><EFBFBD>?
|
||||
HttpdnsIpObject *retrievedIpv4 = hostObject.ipv4List.firstObject;
|
||||
XCTAssertEqualObjects(retrievedIpv4.ip, @"1.2.3.4", @"IPv4地址应该正确");
|
||||
XCTAssertEqual(retrievedIpv4.detectRT, 50, @"IPv4的detectRT应该正确");
|
||||
@@ -127,7 +127,7 @@
|
||||
HttpdnsHostObject *hostObject = [[HttpdnsHostObject alloc] init];
|
||||
hostObject.host = @"example.com";
|
||||
|
||||
// 创建多个IP对象,具有不同的检测时间
|
||||
// 创建多个IP对象,具有不同的检测时<EFBFBD><EFBFBD>?
|
||||
HttpdnsIpObject *ip1 = [[HttpdnsIpObject alloc] init];
|
||||
ip1.ip = @"1.1.1.1";
|
||||
ip1.detectRT = 100;
|
||||
@@ -142,7 +142,7 @@
|
||||
|
||||
HttpdnsIpObject *ip4 = [[HttpdnsIpObject alloc] init];
|
||||
ip4.ip = @"4.4.4.4";
|
||||
ip4.detectRT = -1; // 未检测
|
||||
ip4.detectRT = -1; // 未检<EFBFBD><EFBFBD>?
|
||||
|
||||
// 添加IP对象到主机对象(顺序不重要)
|
||||
[hostObject addIpv4:ip1];
|
||||
@@ -155,11 +155,11 @@
|
||||
|
||||
// 验证排序结果
|
||||
// 预期顺序:ip2(50ms) -> ip1(100ms) -> ip3(200ms) -> ip4(-1ms)
|
||||
XCTAssertEqual(sortedIps.count, 4, @"应该有4个IP对象");
|
||||
XCTAssertEqualObjects(sortedIps[0].ip, @"2.2.2.2", @"检测时间最短的IP应该排在第一位");
|
||||
XCTAssertEqualObjects(sortedIps[1].ip, @"1.1.1.1", @"检测时间第二短的IP应该排在第二位");
|
||||
XCTAssertEqualObjects(sortedIps[2].ip, @"3.3.3.3", @"检测时间第三短的IP应该排在第三位");
|
||||
XCTAssertEqualObjects(sortedIps[3].ip, @"4.4.4.4", @"未检测的IP应该排在最后");
|
||||
XCTAssertEqual(sortedIps.count, 4, @"应该<EFBFBD><EFBFBD>?个IP对象");
|
||||
XCTAssertEqualObjects(sortedIps[0].ip, @"2.2.2.2", @"检测时间最短的IP应该排在第一<EFBFBD><EFBFBD>?);
|
||||
XCTAssertEqualObjects(sortedIps[1].ip, @"1.1.1.1", @"检测时间第二短的IP应该排在第二<EFBFBD><EFBFBD>?);
|
||||
XCTAssertEqualObjects(sortedIps[2].ip, @"3.3.3.3", @"检测时间第三短的IP应该排在第三<EFBFBD><EFBFBD>?);
|
||||
XCTAssertEqualObjects(sortedIps[3].ip, @"4.4.4.4", @"未检测的IP应该排在最<EFBFBD><EFBFBD>?);
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// ManuallyCleanCacheTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/6/17.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// MultithreadCorrectnessTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/5/26.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -36,7 +36,7 @@
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
// 非阻塞接口不能阻塞调用线程
|
||||
// 非阻塞接口不能阻塞调用线<EFBFBD><EFBFBD>?
|
||||
- (void)testNoneBlockingMethodShouldNotBlock {
|
||||
HttpdnsRequestManager *requestManager = self.httpdns.requestManager;
|
||||
HttpdnsRequestManager *mockedScheduler = OCMPartialMock(requestManager);
|
||||
@@ -132,7 +132,7 @@
|
||||
OCMStub([mockResolver resolve:[OCMArg any] error:(NSError * __autoreleasing *)[OCMArg anyPointer]])
|
||||
.ignoringNonObjectArgs()
|
||||
.andDo(^(NSInvocation *invocation) {
|
||||
// 第一次调用,阻塞1.5秒
|
||||
// 第一次调用,阻塞1.5<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:1.5];
|
||||
[invocation setReturnValue:&mockResolverHostObjects];
|
||||
});
|
||||
@@ -146,7 +146,7 @@
|
||||
[self.httpdns resolveHostSync:ipv4OnlyHost byIpType:HttpdnsQueryIPTypeIpv4];
|
||||
});
|
||||
|
||||
// 确保第一个请求已经开始
|
||||
// 确保第一个请求已经开<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970];
|
||||
@@ -156,7 +156,7 @@
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
// 第二次请求,由于是同一个域名,所以它应该等待第一个请求的返回
|
||||
// 第一个请求返回后,第二个请求不应该再次请求,而是直接从缓存中读取到结果,返回
|
||||
// 所以它的等待时间接近1秒
|
||||
// 所以它的等待时间接<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?
|
||||
HttpdnsResult *result = [self.httpdns resolveHostSync:ipv4OnlyHost byIpType:HttpdnsQueryIPTypeIpv4];
|
||||
XCTAssertNotNil(result);
|
||||
XCTAssertTrue([result.host isEqualToString:ipv4OnlyHost]);
|
||||
@@ -171,7 +171,7 @@
|
||||
XCTAssert(elapsedTime >= 1, @"elapsedTime should be more than 1s, but is %f", elapsedTime);
|
||||
XCTAssert(elapsedTime <= 1.5, @"elapsedTime should not be more than 1.5s, but is %f", elapsedTime);
|
||||
|
||||
// TODO 这里暂时无法跑过,因为现在锁的机制,会导致第二个请求也要去请求
|
||||
// TODO 这里暂时无法跑过,因为现在锁的机制,会导致第二个请求也要去请<EFBFBD><EFBFBD>?
|
||||
// XCTAssert(elapsedTime < 4.1, @"elapsedTime should be less than 4.1s, but is %f", elapsedTime);
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
// 第一次调用,返回异常
|
||||
@throw [NSException exceptionWithName:@"TestException" reason:@"TestException" userInfo:nil];
|
||||
} else {
|
||||
// 第二次调用
|
||||
// 第二次调<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:0.4];
|
||||
[invocation setReturnValue:&mockResolverHostObjects];
|
||||
}
|
||||
@@ -206,7 +206,7 @@
|
||||
[self.httpdns resolveHostSync:ipv4OnlyHost byIpType:HttpdnsQueryIPTypeIpv4];
|
||||
});
|
||||
|
||||
// 确保第一个请求已经开始
|
||||
// 确保第一个请求已经开<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:0.2];
|
||||
|
||||
NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970];
|
||||
@@ -215,8 +215,8 @@
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
// 第二次请求,由于是同一个域名,所以它应该等待第一个请求的返回
|
||||
// 第一个请求失败后,第二个请求从缓存拿不到结果,应该再次请求
|
||||
// 所以它等待的时间将是约5秒
|
||||
// 第一个请求失败后,第二个请求从缓存拿不到结果,应该再次请<EFBFBD><EFBFBD>?
|
||||
// 所以它等待的时间将是约5<EFBFBD><EFBFBD>?
|
||||
HttpdnsResult *result = [self.httpdns resolveHostSync:ipv4OnlyHost byIpType:HttpdnsQueryIPTypeIpv4];
|
||||
XCTAssertNotNil(result);
|
||||
XCTAssertTrue([result.host isEqualToString:ipv4OnlyHost]);
|
||||
@@ -232,7 +232,7 @@
|
||||
XCTAssert(elapsedTime < 0.8, @"elapsedTime should be less than 0.8s, but is %f", elapsedTime);
|
||||
}
|
||||
|
||||
// 同步接口设置最大等待时间
|
||||
// 同步接口设置最大等待时<EFBFBD><EFBFBD>?
|
||||
- (void)testSyncMethodSetBlockTimeout {
|
||||
HttpdnsRequestManager *requestManager = self.httpdns.requestManager;
|
||||
[self.httpdns cleanAllHostCache];
|
||||
@@ -266,7 +266,7 @@
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
// 限制设置的等待时间在一个合理范围,目前是0.5 - 5秒
|
||||
// 限制设置的等待时间在一个合理范围,目前<EFBFBD><EFBFBD>?.5 - 5<EFBFBD><EFBFBD>?
|
||||
- (void)testLimitResolveTimeoutRange {
|
||||
HttpdnsRequest *request = [HttpdnsRequest new];
|
||||
request.host = ipv4OnlyHost;
|
||||
@@ -285,7 +285,7 @@
|
||||
XCTAssertEqual(request.resolveTimeoutInSecond, 3.5);
|
||||
}
|
||||
|
||||
// 设置异步回调接口的最大回调等待时间
|
||||
// 设置异步回调接口的最大回调等待时<EFBFBD><EFBFBD>?
|
||||
- (void)testAsyncMethodSetBlockTimeout {
|
||||
HttpdnsRequestManager *requestManager = self.httpdns.requestManager;
|
||||
[self.httpdns cleanAllHostCache];
|
||||
@@ -318,7 +318,7 @@
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
// 多线程状态下每个线程的等待时间
|
||||
// 多线程状态下每个线程的等待时<EFBFBD><EFBFBD>?
|
||||
- (void)testMultiThreadSyncMethodMaxBlockingTime {
|
||||
HttpdnsRequestManager *requestManager = self.httpdns.requestManager;
|
||||
[self.httpdns cleanAllHostCache];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// PresetCacheAndRetrieveTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/5/26.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/**
|
||||
* 由于使用OCMock在连续的测试用例中重复Mock对象(即使每次都已经stopMocking)会有内存错乱的问题,
|
||||
* 目前还解决不了,所以这个类中的测试case,需要手动单独执行
|
||||
* 目前还解决不了,所以这个类中的测试case,需要手动单独执<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
@interface PresetCacheAndRetrieveTest : TestBase
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
XCTAssertTrue([result.ipv6s[0] isEqualToString:ipv61]);
|
||||
|
||||
// 请求类型为auto,注意,我们认为ipv6only只存在理论上,比如实验室环境
|
||||
// 因此,ipv4的地址是一定会去解析的,auto的作用在于,如果发现网络还支持ipv6,那就多获取ipv6的结果
|
||||
// 因此,ipv4的地址是一定会去解析的,auto的作用在于,如果发现网络还支持ipv6,那就多获取ipv6的结<EFBFBD><EFBFBD>?
|
||||
// 因此,这里得到的也是ipv4+ipv6
|
||||
result = [self.httpdns resolveHostSyncNonBlocking:ipv4AndIpv6Host byIpType:HttpdnsQueryIPTypeAuto];
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
XCTAssertTrue([result.ipv6s[0] isEqualToString:ipv61]);
|
||||
}
|
||||
|
||||
// ttl、lastLookupTime,ipv4和ipv6是分开处理的
|
||||
// ttl、lastLookupTime,ipv4和ipv6是分开处理<EFBFBD><EFBFBD>?
|
||||
- (void)testTTLAndLastLookUpTime {
|
||||
[self presetNetworkEnvAsIpv4AndIpv6];
|
||||
[self.httpdns cleanAllHostCache];
|
||||
@@ -195,7 +195,7 @@
|
||||
hostObject1.lastIPv4LookupTime = currentTimestamp - 1;
|
||||
hostObject1.lastIPv6LookupTime = currentTimestamp - 2;
|
||||
|
||||
// 第一次设置缓存
|
||||
// 第一次设置缓<EFBFBD><EFBFBD>?
|
||||
[self.httpdns.requestManager mergeLookupResultToManager:hostObject1 host:ipv4AndIpv6Host cacheKey:ipv4AndIpv6Host underQueryIpType:HttpdnsQueryIPTypeBoth];
|
||||
|
||||
// auto在当前环境下即请求ipv4和ipv6
|
||||
@@ -210,10 +210,10 @@
|
||||
hostObject2.v4ttl = 600;
|
||||
hostObject2.lastIPv4LookupTime = currentTimestamp - 10;
|
||||
|
||||
// 单独在缓存更新ipv4地址的相关信息
|
||||
// 单独在缓存更新ipv4地址的相关信<EFBFBD><EFBFBD>?
|
||||
[self.httpdns.requestManager mergeLookupResultToManager:hostObject2 host:ipv4AndIpv6Host cacheKey:ipv4AndIpv6Host underQueryIpType:HttpdnsQueryIPTypeIpv4];
|
||||
|
||||
// v4的信息发生变化,v6的信息保持不变
|
||||
// v4的信息发生变化,v6的信息保持不<EFBFBD><EFBFBD>?
|
||||
result = [self.httpdns resolveHostSyncNonBlocking:ipv4AndIpv6Host byIpType:HttpdnsQueryIPTypeAuto];
|
||||
XCTAssertEqual(result.ttl, hostObject2.v4ttl);
|
||||
XCTAssertEqual(result.lastUpdatedTimeInterval, hostObject2.lastIPv4LookupTime);
|
||||
@@ -222,7 +222,7 @@
|
||||
}
|
||||
|
||||
// 只缓存ipv4单栈的地址,按请求双栈类型存入,此时会标记该域名没有ipv6地址
|
||||
// 按预期,会判断该域名没有ipv6地址,因此不会返回ipv6地址,也不会发请求
|
||||
// 按预期,会判断该域名没有ipv6地址,因此不会返回ipv6地址,也不会发请<EFBFBD><EFBFBD>?
|
||||
- (void)testMergeNoIpv6ResultAndGetBoth {
|
||||
[self presetNetworkEnvAsIpv4AndIpv6];
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// ResolvingEffectiveHostTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/5/28.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -42,8 +42,8 @@
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (int64_t)httpdnsHost:(NSString *)host ipType:(AlicloudHttpDNS_IPType)ipType ttl:(int64_t)ttl {
|
||||
// 为了在并发测试中域名快速过期,将ttl设置为随机1-4秒
|
||||
- (int64_t)httpdnsHost:(NSString *)host ipType:(TrustHttpDNS_IPType)ipType ttl:(int64_t)ttl {
|
||||
// 为了在并发测试中域名快速过期,将ttl设置为随<EFBFBD><EFBFBD>?-4<EFBFBD><EFBFBD>?
|
||||
return arc4random_uniform(4) + 1;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
long long executeStartTimeInMs = [[NSDate date] timeIntervalSince1970] * 1000;
|
||||
HttpdnsResult *result = [self.httpdns resolveHostSyncNonBlocking:host byIpType:HttpdnsQueryIPTypeIpv4];
|
||||
long long executeEndTimeInMs = [[NSDate date] timeIntervalSince1970] * 1000;
|
||||
// 非阻塞接口任何情况下不应该阻塞超过30ms
|
||||
// 非阻塞接口任何情况下不应该阻塞超<EFBFBD><EFBFBD>?0ms
|
||||
if (executeEndTimeInMs - executeStartTimeInMs >= 30) {
|
||||
printf("XCTAssertWillFailed, host: %s, executeTime: %lldms\n", [host UTF8String], executeEndTimeInMs - executeStartTimeInMs);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// ScheduleCenterV4Test.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/6/16.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
/**
|
||||
* 由于使用OCMock在连续的测试用例中重复Mock对象(即使每次都已经stopMocking)会有内存错乱的问题,
|
||||
* 目前还解决不了,所以这个类中的测试case,需要手动单独执行
|
||||
* 目前还解决不了,所以这个类中的测试case,需要手动单独执<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
@interface ScheduleCenterV4Test : TestBase
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// ScheduleCenterV6Test.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/6/17.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <OCMock/OCMock.h>
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
/**
|
||||
* 由于使用OCMock在连续的测试用例中重复Mock对象(即使每次都已经stopMocking)会有内存错乱的问题,
|
||||
* 目前还解决不了,所以这个类中的测试case,需要手动单独执行
|
||||
* 目前还解决不了,所以这个类中的测试case,需要手动单独执<EFBFBD><EFBFBD>?
|
||||
*/
|
||||
@interface ScheduleCenterV6Test : TestBase
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// SdnsScenarioTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2024/5/29.
|
||||
// Copyright © 2024 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -41,8 +41,8 @@ static NSString *sdnsHost = @"sdns1.onlyforhttpdnstest.run.place";
|
||||
self.currentTimeStamp = [[NSDate date] timeIntervalSince1970];
|
||||
}
|
||||
|
||||
- (int64_t)httpdnsHost:(NSString *)host ipType:(AlicloudHttpDNS_IPType)ipType ttl:(int64_t)ttl {
|
||||
// 在测试中域名快速过期
|
||||
- (int64_t)httpdnsHost:(NSString *)host ipType:(TrustHttpDNS_IPType)ipType ttl:(int64_t)ttl {
|
||||
// 在测试中域名快速过<EFBFBD><EFBFBD>?
|
||||
return ttlForTest;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// IpDetectorTest.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// Created by xuyecan on 2025/3/14.
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -25,14 +25,14 @@
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
#pragma mark - 单例和基本属性测试
|
||||
#pragma mark - 单例和基本属性测<EFBFBD><EFBFBD>?
|
||||
|
||||
- (void)testSharedInstance {
|
||||
// 测试单例模式
|
||||
HttpdnsIPQualityDetector *detector1 = [HttpdnsIPQualityDetector sharedInstance];
|
||||
HttpdnsIPQualityDetector *detector2 = [HttpdnsIPQualityDetector sharedInstance];
|
||||
|
||||
XCTAssertEqual(detector1, detector2, @"单例模式应该返回相同的实例");
|
||||
XCTAssertEqual(detector1, detector2, @"单例模式应该返回相同的实<EFBFBD><EFBFBD>?);
|
||||
XCTAssertNotNil(detector1, @"单例实例不应为nil");
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
// 测试连接到有效IP
|
||||
HttpdnsIPQualityDetector *detector = [HttpdnsIPQualityDetector sharedInstance];
|
||||
|
||||
// 使用公共DNS服务器作为测试目标
|
||||
// 使用公共DNS服务器作为测试目<EFBFBD><EFBFBD>?
|
||||
NSInteger costTime = [detector tcpConnectToIP:@"8.8.8.8" port:53];
|
||||
|
||||
// 验证连接成功并返回正数耗时
|
||||
@@ -56,8 +56,8 @@
|
||||
// 使用无效IP地址
|
||||
NSInteger costTime = [detector tcpConnectToIP:@"192.168.255.255" port:12345];
|
||||
|
||||
// 验证连接失败并返回-1
|
||||
XCTAssertEqual(costTime, -1, @"连接到无效IP应返回-1");
|
||||
// 验证连接失败并返<EFBFBD><EFBFBD>?1
|
||||
XCTAssertEqual(costTime, -1, @"连接到无效IP应返<EFBFBD><EFBFBD>?1");
|
||||
}
|
||||
|
||||
- (void)testTcpConnectWithInvalidParameters {
|
||||
@@ -66,21 +66,21 @@
|
||||
|
||||
// 测试空IP
|
||||
NSInteger costTime = [detector tcpConnectToIP:nil port:80];
|
||||
XCTAssertEqual(costTime, -1, @"使用nil IP应返回-1");
|
||||
XCTAssertEqual(costTime, -1, @"使用nil IP应返<EFBFBD><EFBFBD>?1");
|
||||
|
||||
// 测试无效格式的IP
|
||||
costTime = [detector tcpConnectToIP:@"not-an-ip" port:80];
|
||||
XCTAssertEqual(costTime, -1, @"使用无效格式IP应返回-1");
|
||||
XCTAssertEqual(costTime, -1, @"使用无效格式IP应返<EFBFBD><EFBFBD>?1");
|
||||
|
||||
// 测试无效端口
|
||||
costTime = [detector tcpConnectToIP:@"8.8.8.8" port:-1];
|
||||
XCTAssertEqual(costTime, -1, @"使用无效端口应返回-1");
|
||||
XCTAssertEqual(costTime, -1, @"使用无效端口应返<EFBFBD><EFBFBD>?1");
|
||||
}
|
||||
|
||||
#pragma mark - 任务调度测试
|
||||
|
||||
- (void)testScheduleIPQualityDetection {
|
||||
// 测试调度IP质量检测任务
|
||||
// 测试调度IP质量检测任<EFBFBD><EFBFBD>?
|
||||
HttpdnsIPQualityDetector *detector = [HttpdnsIPQualityDetector sharedInstance];
|
||||
id detectorMock = OCMPartialMock(detector);
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
HttpdnsIPQualityDetector *detector = [HttpdnsIPQualityDetector sharedInstance];
|
||||
id detectorMock = OCMPartialMock(detector);
|
||||
|
||||
// 设置期望:executeDetection方法不应被调用
|
||||
// 设置期望:executeDetection方法不应被调<EFBFBD><EFBFBD>?
|
||||
OCMReject([detectorMock executeDetection:[OCMArg any]
|
||||
ip:[OCMArg any]
|
||||
port:[OCMArg any]
|
||||
@@ -146,7 +146,7 @@
|
||||
HttpdnsIPQualityDetector *detector = [HttpdnsIPQualityDetector sharedInstance];
|
||||
id detectorMock = OCMPartialMock(detector);
|
||||
|
||||
// 由于无法直接模拟dispatch_semaphore_wait,我们采用另一种方式测试并发限制
|
||||
// 由于无法直接模拟dispatch_semaphore_wait,我们采用另一种方式测试并发限<EFBFBD><EFBFBD>?
|
||||
// 模拟scheduleIPQualityDetection内部实现,当调用时直接执行addPendingTask
|
||||
OCMStub([detectorMock scheduleIPQualityDetection:[OCMArg any]
|
||||
ip:[OCMArg any]
|
||||
@@ -163,7 +163,7 @@
|
||||
[invocation getArgument:&port atIndex:4];
|
||||
[invocation getArgument:&callback atIndex:5];
|
||||
|
||||
// 直接调用addPendingTask,模拟并发限制已达到的情况
|
||||
// 直接调用addPendingTask,模拟并发限制已达到的情<EFBFBD><EFBFBD>?
|
||||
[detector addPendingTask:cacheKey ip:ip port:port callback:callback];
|
||||
});
|
||||
|
||||
@@ -193,14 +193,14 @@
|
||||
}
|
||||
|
||||
- (void)testAddPendingTask {
|
||||
// 测试添加待处理任务
|
||||
// 测试添加待处理任<EFBFBD><EFBFBD>?
|
||||
HttpdnsIPQualityDetector *detector = [HttpdnsIPQualityDetector sharedInstance];
|
||||
id detectorMock = OCMPartialMock(detector);
|
||||
|
||||
// 模拟processPendingTasksIfNeeded方法,避免实际处理任务
|
||||
// 模拟processPendingTasksIfNeeded方法,避免实际处理任<EFBFBD><EFBFBD>?
|
||||
OCMStub([detectorMock processPendingTasksIfNeeded]);
|
||||
|
||||
// 记录初始待处理任务数量
|
||||
// 记录初始待处理任务数<EFBFBD><EFBFBD>?
|
||||
NSUInteger initialCount = [detector pendingTasksCount];
|
||||
|
||||
// 添加一个待处理任务
|
||||
@@ -209,8 +209,8 @@
|
||||
port:[NSNumber numberWithInt:80]
|
||||
callback:^(NSString *cacheKey, NSString *ip, NSInteger costTime) {}];
|
||||
|
||||
// 验证待处理任务数量增加
|
||||
XCTAssertEqual([detector pendingTasksCount], initialCount + 1, @"添加任务后待处理任务数量应增加1");
|
||||
// 验证待处理任务数量增<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([detector pendingTasksCount], initialCount + 1, @"添加任务后待处理任务数量应增<EFBFBD><EFBFBD>?");
|
||||
|
||||
// 停止模拟
|
||||
[detectorMock stopMocking];
|
||||
@@ -221,7 +221,7 @@
|
||||
HttpdnsIPQualityDetector *detector = [HttpdnsIPQualityDetector sharedInstance];
|
||||
id detectorMock = OCMPartialMock(detector);
|
||||
|
||||
// 模拟executeDetection方法,避免实际执行检测
|
||||
// 模拟executeDetection方法,避免实际执行检<EFBFBD><EFBFBD>?
|
||||
OCMStub([detectorMock executeDetection:[OCMArg any]
|
||||
ip:[OCMArg any]
|
||||
port:[OCMArg any]
|
||||
@@ -233,13 +233,13 @@
|
||||
port:[NSNumber numberWithInt:80]
|
||||
callback:^(NSString *cacheKey, NSString *ip, NSInteger costTime) {}];
|
||||
|
||||
// 手动触发处理待处理任务
|
||||
// 手动触发处理待处理任<EFBFBD><EFBFBD>?
|
||||
[detectorMock processPendingTasksIfNeeded];
|
||||
|
||||
// 给处理任务一些时间
|
||||
// 给处理任务一些时<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:0.1];
|
||||
|
||||
// 验证待处理任务已被处理
|
||||
// 验证待处理任务已被处<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([detector pendingTasksCount], 0, @"处理后待处理任务数量应为0");
|
||||
|
||||
// 停止模拟
|
||||
@@ -253,7 +253,7 @@
|
||||
HttpdnsIPQualityDetector *detector = [HttpdnsIPQualityDetector sharedInstance];
|
||||
id detectorMock = OCMPartialMock(detector);
|
||||
|
||||
// 模拟tcpConnectToIP方法返回固定值
|
||||
// 模拟tcpConnectToIP方法返回固定<EFBFBD><EFBFBD>?
|
||||
OCMStub([detectorMock tcpConnectToIP:@"1.2.3.4" port:80]).andReturn(100);
|
||||
|
||||
// 创建期望
|
||||
@@ -265,9 +265,9 @@
|
||||
port:[NSNumber numberWithInt:80]
|
||||
callback:^(NSString *cacheKey, NSString *ip, NSInteger costTime) {
|
||||
// 验证回调参数
|
||||
XCTAssertEqualObjects(cacheKey, @"example.com", @"回调中的cacheKey应正确");
|
||||
XCTAssertEqualObjects(ip, @"1.2.3.4", @"回调中的IP应正确");
|
||||
XCTAssertEqual(costTime, 100, @"回调中的耗时应正确");
|
||||
XCTAssertEqualObjects(cacheKey, @"example.com", @"回调中的cacheKey应正<EFBFBD><EFBFBD>?);
|
||||
XCTAssertEqualObjects(ip, @"1.2.3.4", @"回调中的IP应正<EFBFBD><EFBFBD>?);
|
||||
XCTAssertEqual(costTime, 100, @"回调中的耗时应正<EFBFBD><EFBFBD>?);
|
||||
|
||||
[expectation fulfill];
|
||||
}];
|
||||
@@ -296,8 +296,8 @@
|
||||
port:[NSNumber numberWithInt:80]
|
||||
callback:^(NSString *cacheKey, NSString *ip, NSInteger costTime) {
|
||||
// 验证回调参数
|
||||
XCTAssertEqualObjects(cacheKey, @"example.com", @"回调中的cacheKey应正确");
|
||||
XCTAssertEqualObjects(ip, @"1.2.3.4", @"回调中的IP应正确");
|
||||
XCTAssertEqualObjects(cacheKey, @"example.com", @"回调中的cacheKey应正<EFBFBD><EFBFBD>?);
|
||||
XCTAssertEqualObjects(ip, @"1.2.3.4", @"回调中的IP应正<EFBFBD><EFBFBD>?);
|
||||
XCTAssertEqual(costTime, -1, @"连接失败时回调中的耗时应为-1");
|
||||
|
||||
[expectation fulfill];
|
||||
@@ -311,11 +311,11 @@
|
||||
}
|
||||
|
||||
- (void)testExecuteDetectionWithNilPort {
|
||||
// 测试执行检测时端口为nil的情况
|
||||
// 测试执行检测时端口为nil的情<EFBFBD><EFBFBD>?
|
||||
HttpdnsIPQualityDetector *detector = [HttpdnsIPQualityDetector sharedInstance];
|
||||
id detectorMock = OCMPartialMock(detector);
|
||||
|
||||
// 模拟tcpConnectToIP方法,验证使用默认端口80
|
||||
// 模拟tcpConnectToIP方法,验证使用默认端<EFBFBD><EFBFBD>?0
|
||||
OCMExpect([detectorMock tcpConnectToIP:@"1.2.3.4" port:80]).andReturn(100);
|
||||
|
||||
// 创建期望
|
||||
@@ -356,9 +356,9 @@
|
||||
|
||||
// 模拟tcpConnectToIP方法,延迟返回以模拟网络延迟
|
||||
OCMStub([detectorMock tcpConnectToIP:[OCMArg any] port:80]).andDo(^(NSInvocation *invocation) {
|
||||
// 延迟执行,给GC一个机会
|
||||
// 延迟执行,给GC一个机<EFBFBD><EFBFBD>?
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 设置返回值
|
||||
// 设置返回<EFBFBD><EFBFBD>?
|
||||
NSInteger result = 100;
|
||||
[invocation setReturnValue:&result];
|
||||
});
|
||||
@@ -373,24 +373,24 @@
|
||||
port:[NSNumber numberWithInt:80]
|
||||
callback:^(NSString *cacheKey, NSString *ip, NSInteger costTime) {
|
||||
// 验证对象在回调时仍然有效
|
||||
XCTAssertEqualObjects(cacheKey, @"example.com", @"回调中的cacheKey应正确");
|
||||
XCTAssertEqualObjects(ip, @"1.2.3.4", @"回调中的IP应正确");
|
||||
XCTAssertEqualObjects(cacheKey, @"example.com", @"回调中的cacheKey应正<EFBFBD><EFBFBD>?);
|
||||
XCTAssertEqualObjects(ip, @"1.2.3.4", @"回调中的IP应正<EFBFBD><EFBFBD>?);
|
||||
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
// 清除局部变量的强引用
|
||||
// 清除局部变量的强引<EFBFBD><EFBFBD>?
|
||||
tempCacheKey = nil;
|
||||
tempIP = nil;
|
||||
|
||||
// 强制GC(注意:在ARC下这不一定会立即触发)
|
||||
// 强制GC(注意:在ARC下这不一定会立即触发<EFBFBD><EFBFBD>?
|
||||
@autoreleasepool {
|
||||
// 触发自动释放池
|
||||
// 触发自动释放<EFBFBD><EFBFBD>?
|
||||
}
|
||||
|
||||
// 验证对象没有被释放(应该被executeDetection方法内部强引用)
|
||||
XCTAssertNotNil(weakCacheKey, @"cacheKey不应被释放");
|
||||
XCTAssertNotNil(weakIP, @"IP不应被释放");
|
||||
XCTAssertNotNil(weakCacheKey, @"cacheKey不应被释<EFBFBD><EFBFBD>?);
|
||||
XCTAssertNotNil(weakIP, @"IP不应被释<EFBFBD><EFBFBD>?);
|
||||
|
||||
// 等待异步操作完成
|
||||
[self waitForExpectationsWithTimeout:5.0 handler:nil];
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClientTestBase.h
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
// 测试基类 - 为所有 HttpdnsNWHTTPClient 测试提供共享的 setup/teardown
|
||||
// 测试基类 - 为所<EFBFBD><EFBFBD>?HttpdnsNWHTTPClient 测试提供共享<EFBFBD><EFBFBD>?setup/teardown
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClientTestBase.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
// 测试基类实现 - 共享的环境配置与清理逻辑
|
||||
//
|
||||
// 注意:所有测试需要先启动本地 mock server
|
||||
// 启动命令:cd AlicloudHttpDNSTests/Network && python3 mock_server.py
|
||||
// 服务端口:
|
||||
// 启动命令:cd TrustHttpDNSTests/Network && python3 mock_server.py
|
||||
// 服务端口<EFBFBD><EFBFBD>?
|
||||
// - HTTP: 11080
|
||||
// - HTTPS: 11443, 11444, 11445, 11446
|
||||
//
|
||||
@@ -21,18 +21,18 @@
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
|
||||
// 设置环境变量以跳过 TLS 验证(用于本地 mock server 的自签名证书)
|
||||
// 这是安全的,因为:
|
||||
// 设置环境变量以跳<EFBFBD><EFBFBD>?TLS 验证(用于本<EFBFBD><EFBFBD>?mock server 的自签名证书<EFBFBD><EFBFBD>?
|
||||
// 这是安全的,因为<EFBFBD><EFBFBD>?
|
||||
// 1. 仅在测试环境生效
|
||||
// 2. 连接限制为本地 loopback (127.0.0.1)
|
||||
// 3. 不影响生产代码
|
||||
// 2. 连接限制为本<EFBFBD><EFBFBD>?loopback (127.0.0.1)
|
||||
// 3. 不影响生产代<EFBFBD><EFBFBD>?
|
||||
setenv("HTTPDNS_SKIP_TLS_VERIFY", "1", 1);
|
||||
|
||||
self.client = [[HttpdnsNWHTTPClient alloc] init];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// 清除环境变量,避免影响其他测试
|
||||
// 清除环境变量,避免影响其他测<EFBFBD><EFBFBD>?
|
||||
unsetenv("HTTPDNS_SKIP_TLS_VERIFY");
|
||||
|
||||
self.client = nil;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClientTestHelper.h
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -12,20 +12,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HttpdnsNWHTTPClientTestHelper : NSObject
|
||||
|
||||
#pragma mark - HTTP 响应数据构造
|
||||
#pragma mark - HTTP 响应数据构<EFBFBD><EFBFBD>?
|
||||
|
||||
// 构造标准 HTTP 响应数据
|
||||
// 构造标<EFBFBD><EFBFBD>?HTTP 响应数据
|
||||
+ (NSData *)createHTTPResponseWithStatus:(NSInteger)statusCode
|
||||
statusText:(NSString *)statusText
|
||||
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
|
||||
body:(nullable NSData *)body;
|
||||
|
||||
// 构造 chunked 编码的 HTTP 响应
|
||||
// 构<EFBFBD><EFBFBD>?chunked 编码<EFBFBD><EFBFBD>?HTTP 响应
|
||||
+ (NSData *)createChunkedHTTPResponseWithStatus:(NSInteger)statusCode
|
||||
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
|
||||
chunks:(NSArray<NSData *> *)chunks;
|
||||
|
||||
// 构造 chunked 编码的 HTTP 响应(带 trailers)
|
||||
// 构<EFBFBD><EFBFBD>?chunked 编码<EFBFBD><EFBFBD>?HTTP 响应(带 trailers<EFBFBD><EFBFBD>?
|
||||
+ (NSData *)createChunkedHTTPResponseWithStatus:(NSInteger)statusCode
|
||||
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
|
||||
chunks:(NSArray<NSData *> *)chunks
|
||||
@@ -36,18 +36,18 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// 编码单个 chunk
|
||||
+ (NSData *)encodeChunk:(NSData *)data;
|
||||
|
||||
// 编码单个 chunk(带 extension)
|
||||
// 编码单个 chunk(带 extension<EFBFBD><EFBFBD>?
|
||||
+ (NSData *)encodeChunk:(NSData *)data extension:(nullable NSString *)extension;
|
||||
|
||||
// 编码终止 chunk(size=0)
|
||||
// 编码终止 chunk(size=0<EFBFBD><EFBFBD>?
|
||||
+ (NSData *)encodeLastChunk;
|
||||
|
||||
// 编码终止 chunk(带 trailers)
|
||||
// 编码终止 chunk(带 trailers<EFBFBD><EFBFBD>?
|
||||
+ (NSData *)encodeLastChunkWithTrailers:(NSDictionary<NSString *, NSString *> *)trailers;
|
||||
|
||||
#pragma mark - 测试数据生成
|
||||
|
||||
// 生成指定大小的随机数据
|
||||
// 生成指定大小的随机数<EFBFBD><EFBFBD>?
|
||||
+ (NSData *)randomDataWithSize:(NSUInteger)size;
|
||||
|
||||
// 生成 JSON 格式的响应体
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClientTestHelper.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpdnsNWHTTPClientTestHelper.h"
|
||||
|
||||
@implementation HttpdnsNWHTTPClientTestHelper
|
||||
|
||||
#pragma mark - HTTP 响应数据构造
|
||||
#pragma mark - HTTP 响应数据构<EFBFBD><EFBFBD>?
|
||||
|
||||
+ (NSData *)createHTTPResponseWithStatus:(NSInteger)statusCode
|
||||
statusText:(NSString *)statusText
|
||||
@@ -28,12 +28,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有 body 但没有 Content-Length,自动添加
|
||||
// 如果<EFBFBD><EFBFBD>?body 但没<EFBFBD><EFBFBD>?Content-Length,自动添<EFBFBD><EFBFBD>?
|
||||
if (body && body.length > 0 && !headers[@"Content-Length"]) {
|
||||
[response appendFormat:@"Content-Length: %lu\r\n", (unsigned long)body.length];
|
||||
}
|
||||
|
||||
// 空行分隔头部和 body
|
||||
// 空行分隔头部<EFBFBD><EFBFBD>?body
|
||||
[response appendString:@"\r\n"];
|
||||
|
||||
NSMutableData *responseData = [[response dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
|
||||
@@ -144,7 +144,7 @@
|
||||
+ (NSData *)randomDataWithSize:(NSUInteger)size {
|
||||
NSMutableData *data = [NSMutableData dataWithLength:size];
|
||||
if (SecRandomCopyBytes(kSecRandomDefault, size, data.mutableBytes) != 0) {
|
||||
// 如果 SecRandom 失败,使用简单的随机数
|
||||
// 如果 SecRandom 失败,使用简单的随机<EFBFBD><EFBFBD>?
|
||||
uint8_t *bytes = data.mutableBytes;
|
||||
for (NSUInteger i = 0; i < size; i++) {
|
||||
bytes[i] = arc4random_uniform(256);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClientTests.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
#pragma mark - A. HTTP 解析逻辑测试
|
||||
|
||||
#pragma mark - A1. Header 解析 (9个)
|
||||
#pragma mark - A1. Header 解析 (9<EFBFBD><EFBFBD>?
|
||||
|
||||
// A1.1 正常响应
|
||||
- (void)testParseHTTPHeaders_ValidResponse_Success {
|
||||
@@ -88,7 +88,7 @@
|
||||
|
||||
XCTAssertEqual(result, HttpdnsHTTPHeaderParseResultSuccess);
|
||||
XCTAssertEqual(headers.count, testHeaders.count);
|
||||
// 验证所有头部都被解析,且 key 转为小写
|
||||
// 验证所有头部都被解析,<EFBFBD><EFBFBD>?key 转为小写
|
||||
XCTAssertEqualObjects(headers[@"content-type"], @"application/json");
|
||||
XCTAssertEqualObjects(headers[@"content-length"], @"123");
|
||||
XCTAssertEqualObjects(headers[@"connection"], @"keep-alive");
|
||||
@@ -96,7 +96,7 @@
|
||||
XCTAssertEqualObjects(headers[@"cache-control"], @"no-cache");
|
||||
}
|
||||
|
||||
// A1.3 不完整响应
|
||||
// A1.3 不完整响<EFBFBD><EFBFBD>?
|
||||
- (void)testParseHTTPHeaders_IncompleteData_ReturnsIncomplete {
|
||||
NSString *incompleteResponse = @"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n";
|
||||
NSData *data = [incompleteResponse dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -152,10 +152,10 @@
|
||||
error:&error];
|
||||
|
||||
XCTAssertEqual(result, HttpdnsHTTPHeaderParseResultSuccess);
|
||||
XCTAssertEqualObjects(headers[@"content-type"], @"application/json"); // 应该被 trim
|
||||
XCTAssertEqualObjects(headers[@"content-type"], @"application/json"); // 应该<EFBFBD><EFBFBD>?trim
|
||||
}
|
||||
|
||||
// A1.6 头部没有值
|
||||
// A1.6 头部没有<EFBFBD><EFBFBD>?
|
||||
- (void)testParseHTTPHeaders_EmptyHeaderValue_HandledGracefully {
|
||||
NSString *responseWithEmptyValue = @"HTTP/1.1 200 OK\r\nX-Empty-Header:\r\n\r\n";
|
||||
NSData *data = [responseWithEmptyValue dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -175,7 +175,7 @@
|
||||
XCTAssertEqualObjects(headers[@"x-empty-header"], @"");
|
||||
}
|
||||
|
||||
// A1.7 状态码非数字
|
||||
// A1.7 状态码非数<EFBFBD><EFBFBD>?
|
||||
- (void)testParseHTTPHeaders_NonNumericStatusCode_ReturnsError {
|
||||
NSString *invalidStatusCode = @"HTTP/1.1 ABC OK\r\n\r\n";
|
||||
NSData *data = [invalidStatusCode dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -213,7 +213,7 @@
|
||||
XCTAssertEqual(result, HttpdnsHTTPHeaderParseResultError);
|
||||
}
|
||||
|
||||
// A1.9 头部没有冒号被跳过
|
||||
// A1.9 头部没有冒号被跳<EFBFBD><EFBFBD>?
|
||||
- (void)testParseHTTPHeaders_HeaderWithoutColon_Skipped {
|
||||
NSString *responseWithInvalidHeader = @"HTTP/1.1 200 OK\r\nInvalidHeader\r\nContent-Type: application/json\r\n\r\n";
|
||||
NSData *data = [responseWithInvalidHeader dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -233,7 +233,7 @@
|
||||
XCTAssertEqualObjects(headers[@"content-type"], @"application/json"); // 有效头部正常解析
|
||||
}
|
||||
|
||||
#pragma mark - A2. Chunked 编码检查 (8个)
|
||||
#pragma mark - A2. Chunked 编码检<EFBFBD><EFBFBD>?(8<EFBFBD><EFBFBD>?
|
||||
|
||||
// A2.1 单个 chunk
|
||||
- (void)testCheckChunkedBody_SingleChunk_DetectsComplete {
|
||||
@@ -269,9 +269,9 @@
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
// A2.3 不完整 chunk
|
||||
// A2.3 不完<EFBFBD><EFBFBD>?chunk
|
||||
- (void)testCheckChunkedBody_IncompleteChunk_ReturnsIncomplete {
|
||||
NSString *incompleteChunkBody = @"5\r\nhel"; // 数据不完整
|
||||
NSString *incompleteChunkBody = @"5\r\nhel"; // 数据不完<EFBFBD><EFBFBD>?
|
||||
NSString *response = [NSString stringWithFormat:@"HTTP/1.1 200 OK\r\n\r\n%@", incompleteChunkBody];
|
||||
NSData *data = [response dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
@@ -285,13 +285,13 @@
|
||||
XCTAssertEqual(result, HttpdnsHTTPChunkParseResultIncomplete);
|
||||
}
|
||||
|
||||
// A2.4 带 chunk extension
|
||||
// A2.4 <EFBFBD><EFBFBD>?chunk extension
|
||||
- (void)testCheckChunkedBody_WithChunkExtension_Ignored {
|
||||
NSString *chunkWithExtension = @"5;name=value\r\nhello\r\n0\r\n\r\n";
|
||||
NSString *response = [NSString stringWithFormat:@"HTTP/1.1 200 OK\r\n\r\n%@", chunkWithExtension];
|
||||
NSData *data = [response dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
// headerEndIndex 指向第一个 \r\n\r\n 中的第一个 \r
|
||||
// headerEndIndex 指向第一<EFBFBD><EFBFBD>?\r\n\r\n 中的第一<EFBFBD><EFBFBD>?\r
|
||||
NSUInteger headerEndIndex = [@"HTTP/1.1 200 OK" length];
|
||||
NSError *error;
|
||||
|
||||
@@ -337,9 +337,9 @@
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
// A2.7 缺少 CRLF 终止符
|
||||
// A2.7 缺少 CRLF 终止<EFBFBD><EFBFBD>?
|
||||
- (void)testCheckChunkedBody_MissingCRLFTerminator_ReturnsError {
|
||||
NSString *missingTerminator = @"5\r\nhelloXX0\r\n\r\n"; // 应该是 hello\r\n
|
||||
NSString *missingTerminator = @"5\r\nhelloXX0\r\n\r\n"; // 应该<EFBFBD><EFBFBD>?hello\r\n
|
||||
NSString *response = [NSString stringWithFormat:@"HTTP/1.1 200 OK\r\n\r\n%@", missingTerminator];
|
||||
NSData *data = [response dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
@@ -354,7 +354,7 @@
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
// A2.8 带 trailers
|
||||
// A2.8 <EFBFBD><EFBFBD>?trailers
|
||||
- (void)testCheckChunkedBody_WithTrailers_DetectsComplete {
|
||||
NSString *chunkWithTrailers = @"5\r\nhello\r\n0\r\nX-Trailer: value\r\nX-Custom: test\r\n\r\n";
|
||||
NSString *response = [NSString stringWithFormat:@"HTTP/1.1 200 OK\r\n\r\n%@", chunkWithTrailers];
|
||||
@@ -371,7 +371,7 @@
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
#pragma mark - A3. Chunked 解码 (2个)
|
||||
#pragma mark - A3. Chunked 解码 (2<EFBFBD><EFBFBD>?
|
||||
|
||||
// A3.1 多个 chunks 解码
|
||||
- (void)testDecodeChunkedBody_MultipleChunks_DecodesCorrectly {
|
||||
@@ -384,7 +384,7 @@
|
||||
headers:nil
|
||||
chunks:chunks];
|
||||
|
||||
// 提取 chunked body 部分(跳过 headers)
|
||||
// 提取 chunked body 部分(跳<EFBFBD><EFBFBD>?headers<EFBFBD><EFBFBD>?
|
||||
NSData *headerData = [@"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *bodyData = [chunkedData subdataWithRange:NSMakeRange(headerData.length, chunkedData.length - headerData.length)];
|
||||
|
||||
@@ -409,7 +409,7 @@
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
#pragma mark - A4. 完整响应解析 (6个)
|
||||
#pragma mark - A4. 完整响应解析 (6<EFBFBD><EFBFBD>?
|
||||
|
||||
// A4.1 Content-Length 响应
|
||||
- (void)testParseResponse_WithContentLength_ParsesCorrectly {
|
||||
@@ -467,7 +467,7 @@
|
||||
XCTAssertEqualObjects(bodyString, @"{\"ips\":[]}");
|
||||
}
|
||||
|
||||
// A4.3 空 body
|
||||
// A4.3 <EFBFBD><EFBFBD>?body
|
||||
- (void)testParseResponse_EmptyBody_Success {
|
||||
NSData *responseData = [HttpdnsNWHTTPClientTestHelper createHTTPResponseWithStatus:204
|
||||
statusText:@"No Content"
|
||||
@@ -490,12 +490,12 @@
|
||||
XCTAssertEqual(body.length, 0);
|
||||
}
|
||||
|
||||
// A4.4 Content-Length 不匹配仍然成功
|
||||
// A4.4 Content-Length 不匹配仍然成<EFBFBD><EFBFBD>?
|
||||
- (void)testParseResponse_ContentLengthMismatch_LogsButSucceeds {
|
||||
NSData *bodyData = [@"short" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *responseData = [HttpdnsNWHTTPClientTestHelper createHTTPResponseWithStatus:200
|
||||
statusText:@"OK"
|
||||
headers:@{@"Content-Length": @"100"} // 不匹配
|
||||
headers:@{@"Content-Length": @"100"} // 不匹<EFBFBD><EFBFBD>?
|
||||
body:bodyData];
|
||||
|
||||
NSInteger statusCode;
|
||||
@@ -509,11 +509,11 @@
|
||||
body:&body
|
||||
error:&error];
|
||||
|
||||
XCTAssertTrue(success); // 仍然成功,只是日志警告
|
||||
XCTAssertTrue(success); // 仍然成功,只是日志警<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqualObjects(body, bodyData);
|
||||
}
|
||||
|
||||
// A4.5 空数据返回错误
|
||||
// A4.5 空数据返回错<EFBFBD><EFBFBD>?
|
||||
- (void)testParseResponse_EmptyData_ReturnsError {
|
||||
NSData *emptyData = [NSData data];
|
||||
|
||||
@@ -532,7 +532,7 @@
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
// A4.6 只有 headers 无 body
|
||||
// A4.6 只有 headers <EFBFBD><EFBFBD>?body
|
||||
- (void)testParseResponse_OnlyHeaders_EmptyBody {
|
||||
NSData *responseData = [HttpdnsNWHTTPClientTestHelper createHTTPResponseWithStatus:200
|
||||
statusText:@"OK"
|
||||
@@ -556,7 +556,7 @@
|
||||
XCTAssertEqual(body.length, 0);
|
||||
}
|
||||
|
||||
#pragma mark - C. 请求构建测试 (7个)
|
||||
#pragma mark - C. 请求构建测试 (7<EFBFBD><EFBFBD>?
|
||||
|
||||
// C.1 基本 GET 请求
|
||||
- (void)testBuildHTTPRequest_BasicGET_CorrectFormat {
|
||||
@@ -572,7 +572,7 @@
|
||||
XCTAssertTrue([request hasSuffix:@"\r\n\r\n"]);
|
||||
}
|
||||
|
||||
// C.2 带查询参数
|
||||
// C.2 带查询参<EFBFBD><EFBFBD>?
|
||||
- (void)testBuildHTTPRequest_WithQueryString_Included {
|
||||
NSURL *url = [NSURL URLWithString:@"http://example.com/path?foo=bar&baz=qux"];
|
||||
NSString *request = [self.client buildHTTPRequestStringWithURL:url userAgent:nil];
|
||||
@@ -588,7 +588,7 @@
|
||||
XCTAssertTrue([request containsString:@"User-Agent: CustomAgent/1.0\r\n"]);
|
||||
}
|
||||
|
||||
// C.4 HTTP 默认端口不显示
|
||||
// C.4 HTTP 默认端口不显<EFBFBD><EFBFBD>?
|
||||
- (void)testBuildHTTPRequest_HTTPDefaultPort_NotInHost {
|
||||
NSURL *url = [NSURL URLWithString:@"http://example.com:80/"];
|
||||
NSString *request = [self.client buildHTTPRequestStringWithURL:url userAgent:nil];
|
||||
@@ -597,7 +597,7 @@
|
||||
XCTAssertFalse([request containsString:@"Host: example.com:80\r\n"]);
|
||||
}
|
||||
|
||||
// C.5 HTTPS 默认端口不显示
|
||||
// C.5 HTTPS 默认端口不显<EFBFBD><EFBFBD>?
|
||||
- (void)testBuildHTTPRequest_HTTPSDefaultPort_NotInHost {
|
||||
NSURL *url = [NSURL URLWithString:@"https://example.com:443/"];
|
||||
NSString *request = [self.client buildHTTPRequestStringWithURL:url userAgent:nil];
|
||||
@@ -606,7 +606,7 @@
|
||||
XCTAssertFalse([request containsString:@"Host: example.com:443\r\n"]);
|
||||
}
|
||||
|
||||
// C.6 非默认端口显示
|
||||
// C.6 非默认端口显<EFBFBD><EFBFBD>?
|
||||
- (void)testBuildHTTPRequest_NonDefaultPort_InHost {
|
||||
NSURL *url = [NSURL URLWithString:@"http://example.com:8080/"];
|
||||
NSString *request = [self.client buildHTTPRequestStringWithURL:url userAgent:nil];
|
||||
@@ -627,7 +627,7 @@
|
||||
#pragma mark - E. TLS 验证测试 (4个占位符)
|
||||
|
||||
// 注意:TLS 验证测试需要真实的 SecTrustRef 或复杂的 mock
|
||||
// 这些测试在实际环境中需要根据测试框架调整
|
||||
// 这些测试在实际环境中需要根据测试框架调<EFBFBD><EFBFBD>?
|
||||
|
||||
// E.1 有效证书返回 YES
|
||||
- (void)testEvaluateServerTrust_ValidCertificate_ReturnsYES {
|
||||
@@ -647,10 +647,10 @@
|
||||
|
||||
// E.4 指定域名使用 SSL Policy
|
||||
- (void)testEvaluateServerTrust_WithDomain_UsesSSLPolicy {
|
||||
// 验证使用了 SecPolicyCreateSSL(true, domain)
|
||||
// 验证使用<EFBFBD><EFBFBD>?SecPolicyCreateSSL(true, domain)
|
||||
}
|
||||
|
||||
#pragma mark - F. 边缘情况测试 (5个)
|
||||
#pragma mark - F. 边缘情况测试 (5<EFBFBD><EFBFBD>?
|
||||
|
||||
// F.1 超长 URL
|
||||
- (void)testPerformRequest_VeryLongURL_HandlesCorrectly {
|
||||
@@ -666,7 +666,7 @@
|
||||
XCTAssertTrue(request.length > 5000);
|
||||
}
|
||||
|
||||
// F.2 空 User-Agent
|
||||
// F.2 <EFBFBD><EFBFBD>?User-Agent
|
||||
- (void)testBuildRequest_EmptyUserAgent_NoUserAgentHeader {
|
||||
NSURL *url = [NSURL URLWithString:@"http://example.com/"];
|
||||
NSString *requestWithNil = [self.client buildHTTPRequestStringWithURL:url userAgent:nil];
|
||||
@@ -674,7 +674,7 @@
|
||||
XCTAssertFalse([requestWithNil containsString:@"User-Agent:"]);
|
||||
}
|
||||
|
||||
// F.3 超大响应体
|
||||
// F.3 超大响应<EFBFBD><EFBFBD>?
|
||||
- (void)testParseResponse_VeryLargeBody_HandlesCorrectly {
|
||||
NSData *largeBody = [HttpdnsNWHTTPClientTestHelper randomDataWithSize:5 * 1024 * 1024];
|
||||
NSData *responseData = [HttpdnsNWHTTPClientTestHelper createHTTPResponseWithStatus:200
|
||||
@@ -697,7 +697,7 @@
|
||||
XCTAssertEqual(body.length, largeBody.length);
|
||||
}
|
||||
|
||||
// F.4 Chunked 解码失败回退到原始数据
|
||||
// F.4 Chunked 解码失败回退到原始数<EFBFBD><EFBFBD>?
|
||||
- (void)testParseResponse_ChunkedDecodeFails_FallsBackToRaw {
|
||||
NSString *badChunked = @"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\nBAD_CHUNK_DATA";
|
||||
NSData *responseData = [badChunked dataUsingEncoding:NSUTF8StringEncoding];
|
||||
@@ -717,7 +717,7 @@
|
||||
XCTAssertNotNil(body);
|
||||
}
|
||||
|
||||
// F.5 连接池 key 生成测试
|
||||
// F.5 连接<EFBFBD><EFBFBD>?key 生成测试
|
||||
- (void)testConnectionPoolKey_DifferentHosts_SeparateKeys {
|
||||
NSString *key1 = [self.client connectionPoolKeyForHost:@"host1.com" port:@"80" useTLS:NO];
|
||||
NSString *key2 = [self.client connectionPoolKeyForHost:@"host2.com" port:@"80" useTLS:NO];
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClient_BasicIntegrationTests.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
// 基础集成测试 - 包含基础功能 (G) 和连接复用 (J) 测试组
|
||||
// 测试总数:12 个(G:7 + J:5)
|
||||
// 基础集成测试 - 包含基础功能 (G) 和连接复<EFBFBD><EFBFBD>?(J) 测试<EFBFBD><EFBFBD>?
|
||||
// 测试总数<EFBFBD><EFBFBD>?2 个(G:7 + J:5<EFBFBD><EFBFBD>?
|
||||
//
|
||||
|
||||
#import "HttpdnsNWHTTPClientTestBase.h"
|
||||
@@ -134,7 +134,7 @@
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSError *error = nil;
|
||||
// httpbin.org/stream-bytes 返回 chunked 编码的响应
|
||||
// httpbin.org/stream-bytes 返回 chunked 编码的响<EFBFBD><EFBFBD>?
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/stream-bytes/1024"
|
||||
userAgent:@"HttpdnsNWHTTPClient/1.0"
|
||||
timeout:15.0
|
||||
@@ -145,7 +145,7 @@
|
||||
XCTAssertEqual(response.statusCode, 200);
|
||||
XCTAssertEqual(response.body.length, 1024, @"Should receive exactly 1024 bytes");
|
||||
|
||||
// 验证 Transfer-Encoding 头
|
||||
// 验证 Transfer-Encoding <EFBFBD><EFBFBD>?
|
||||
NSString *transferEncoding = response.headers[@"transfer-encoding"];
|
||||
if (transferEncoding) {
|
||||
XCTAssertTrue([transferEncoding containsString:@"chunked"], @"Should use chunked encoding");
|
||||
@@ -157,7 +157,7 @@
|
||||
[self waitForExpectations:@[expectation] timeout:20.0];
|
||||
}
|
||||
|
||||
#pragma mark - 额外的集成测试
|
||||
#pragma mark - 额外的集成测<EFBFBD><EFBFBD>?
|
||||
|
||||
// G.6 超时测试(可选)
|
||||
- (void)testIntegration_RequestTimeout_ReturnsError {
|
||||
@@ -165,7 +165,7 @@
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSError *error = nil;
|
||||
// httpbin.org/delay/10 会延迟 10 秒响应,我们设置 2 秒超时
|
||||
// httpbin.org/delay/10 会延<EFBFBD><EFBFBD>?10 秒响应,我们设置 2 秒超<EFBFBD><EFBFBD>?
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
|
||||
userAgent:@"HttpdnsNWHTTPClient/1.0"
|
||||
timeout:2.0
|
||||
@@ -180,7 +180,7 @@
|
||||
[self waitForExpectations:@[expectation] timeout:5.0];
|
||||
}
|
||||
|
||||
// G.7 多个不同头部的请求
|
||||
// G.7 多个不同头部的请<EFBFBD><EFBFBD>?
|
||||
- (void)testIntegration_CustomHeaders_Reflected {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Custom headers"];
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
XCTAssertNotNil(response);
|
||||
XCTAssertEqual(response.statusCode, 200);
|
||||
|
||||
// 解析 JSON 响应,验证我们的 User-Agent 被发送
|
||||
// 解析 JSON 响应,验证我们的 User-Agent 被发<EFBFBD><EFBFBD>?
|
||||
NSError *jsonError = nil;
|
||||
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:response.body
|
||||
options:0
|
||||
@@ -212,7 +212,7 @@
|
||||
|
||||
#pragma mark - J. 连接复用详细测试
|
||||
|
||||
// J.1 连接过期测试(31秒后创建新连接)
|
||||
// J.1 连接过期测试<EFBFBD><EFBFBD>?1秒后创建新连接)
|
||||
- (void)testConnectionReuse_Expiry31Seconds_NewConnectionCreated {
|
||||
if (getenv("SKIP_SLOW_TESTS")) {
|
||||
return;
|
||||
@@ -252,12 +252,12 @@
|
||||
[self waitForExpectations:@[expectation] timeout:70.0];
|
||||
}
|
||||
|
||||
// J.2 连接池容量限制验证
|
||||
// J.2 连接池容量限制验<EFBFBD><EFBFBD>?
|
||||
- (void)testConnectionReuse_TenRequests_OnlyFourConnectionsKept {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Pool size limit"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 连续10个请求
|
||||
// 连续10个请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
@@ -267,7 +267,7 @@
|
||||
XCTAssertTrue(response != nil || error != nil);
|
||||
}
|
||||
|
||||
// 等待所有连接归还
|
||||
// 等待所有连接归<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 无法直接验证池大小,但如果实现正确,池应自动限制
|
||||
@@ -330,7 +330,7 @@
|
||||
error:&httpError];
|
||||
XCTAssertTrue(httpResponse != nil || httpError != nil);
|
||||
|
||||
// HTTPS 请求(应该使用不同的连接池 key)
|
||||
// HTTPS 请求(应该使用不同的连接<EFBFBD><EFBFBD>?key<EFBFBD><EFBFBD>?
|
||||
NSError *httpsError = nil;
|
||||
HttpdnsNWHTTPClientResponse *httpsResponse = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
userAgent:@"HTTPS"
|
||||
@@ -345,7 +345,7 @@
|
||||
[self waitForExpectations:@[expectation] timeout:35.0];
|
||||
}
|
||||
|
||||
// J.5 长连接保持测试
|
||||
// J.5 长连接保持测<EFBFBD><EFBFBD>?
|
||||
- (void)testConnectionReuse_TwentyRequestsOneSecondApart_ConnectionKeptAlive {
|
||||
if (getenv("SKIP_SLOW_TESTS")) {
|
||||
return;
|
||||
@@ -359,7 +359,7 @@
|
||||
|
||||
// 20个请求,间隔1秒(第一个请求立即执行)
|
||||
for (NSInteger i = 0; i < 20; i++) {
|
||||
// 除第一个请求外,每次请求前等待1秒
|
||||
// 除第一个请求外,每次请求前等待1<EFBFBD><EFBFBD>?
|
||||
if (i > 0) {
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
}
|
||||
@@ -381,10 +381,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 至少大部分请求应该成功
|
||||
// 至少大部分请求应该成<EFBFBD><EFBFBD>?
|
||||
XCTAssertGreaterThan(successCount, 15, @"Most requests should succeed with connection reuse");
|
||||
|
||||
// 验证连接复用:后续请求应该更快(如果使用了keep-alive)
|
||||
// 验证连接复用:后续请求应该更快(如果使用了keep-alive<EFBFBD><EFBFBD>?
|
||||
if (requestTimes.count >= 10) {
|
||||
double firstRequestTime = [requestTimes[0] doubleValue];
|
||||
double laterAvgTime = 0;
|
||||
@@ -399,7 +399,7 @@
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
// 超时计算: 19秒sleep + 20个请求×~2秒 = 59秒,设置50秒(提前退出机制保证效率)
|
||||
// 超时计算: 19秒sleep + 20个请求×~2<EFBFBD><EFBFBD>?= 59秒,设置50秒(提前退出机制保证效率)
|
||||
[self waitForExpectations:@[expectation] timeout:50.0];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClient_ConcurrencyTests.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
// 并发测试 - 包含并发请求 (H)、竞态条件 (I)、并发多端口 (N) 测试组
|
||||
// 测试总数:13 个(H:5 + I:5 + N:3)
|
||||
// 并发测试 - 包含并发请求 (H)、竞态条<EFBFBD><EFBFBD>?(I)、并发多端口 (N) 测试<EFBFBD><EFBFBD>?
|
||||
// 测试总数<EFBFBD><EFBFBD>?3 个(H:5 + I:5 + N:3<EFBFBD><EFBFBD>?
|
||||
//
|
||||
|
||||
#import "HttpdnsNWHTTPClientTestBase.h"
|
||||
@@ -137,7 +137,7 @@
|
||||
[self waitForExpectations:expectations timeout:40.0];
|
||||
}
|
||||
|
||||
// H.4 高负载压力测试
|
||||
// H.4 高负载压力测<EFBFBD><EFBFBD>?
|
||||
- (void)testConcurrency_HighLoad50Concurrent_NoDeadlock {
|
||||
NSInteger concurrentCount = 50;
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
@@ -169,7 +169,7 @@
|
||||
|
||||
[self waitForExpectations:expectations timeout:60.0];
|
||||
|
||||
// 至少大部分请求应该成功(允许部分失败,因为高负载)
|
||||
// 至少大部分请求应该成功(允许部分失败,因为高负载<EFBFBD><EFBFBD>?
|
||||
XCTAssertGreaterThan(successCount, concurrentCount * 0.8, @"At least 80%% should succeed");
|
||||
}
|
||||
|
||||
@@ -230,14 +230,14 @@
|
||||
[self waitForExpectations:@[serialExpectation, parallel1, parallel2, parallel3] timeout:60.0];
|
||||
}
|
||||
|
||||
#pragma mark - I. 竞态条件测试
|
||||
#pragma mark - I. 竞态条件测<EFBFBD><EFBFBD>?
|
||||
|
||||
// I.1 连接池容量测试
|
||||
// I.1 连接池容量测<EFBFBD><EFBFBD>?
|
||||
- (void)testRaceCondition_ExceedPoolCapacity_MaxFourConnections {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Pool capacity test"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 快速连续发起 10 个请求
|
||||
// 快速连续发<EFBFBD><EFBFBD>?10 个请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
@@ -251,7 +251,7 @@
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 注意:无法直接检查池大小(内部实现),只能通过行为验证
|
||||
// 如果实现正确,池应自动限制为最多 4 个空闲连接
|
||||
// 如果实现正确,池应自动限制为最<EFBFBD><EFBFBD>?4 个空闲连<EFBFBD><EFBFBD>?
|
||||
|
||||
[expectation fulfill];
|
||||
});
|
||||
@@ -276,7 +276,7 @@
|
||||
timeout:15.0
|
||||
error:&error];
|
||||
XCTAssertTrue(response != nil || error != nil);
|
||||
// 连接在这里自动归还
|
||||
// 连接在这里自动归<EFBFBD><EFBFBD>?
|
||||
|
||||
[expectation fulfill];
|
||||
});
|
||||
@@ -284,15 +284,15 @@
|
||||
|
||||
[self waitForExpectations:expectations timeout:30.0];
|
||||
|
||||
// 如果没有崩溃或断言失败,说明并发归还处理正确
|
||||
// 如果没有崩溃或断言失败,说明并发归还处理正<EFBFBD><EFBFBD>?
|
||||
}
|
||||
|
||||
// I.3 获取-归还-再获取竞态
|
||||
// I.3 获取-归还-再获取竞<EFBFBD><EFBFBD>?
|
||||
- (void)testRaceCondition_AcquireReturnReacquire_CorrectState {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Acquire-Return-Reacquire"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 第一个请求
|
||||
// 第一个请<EFBFBD><EFBFBD>?
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"First"
|
||||
@@ -300,7 +300,7 @@
|
||||
error:&error1];
|
||||
XCTAssertTrue(response1 != nil || error1 != nil);
|
||||
|
||||
// 极短暂等待确保连接归还
|
||||
// 极短暂等待确保连接归<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:0.1];
|
||||
|
||||
// 第二个请求应该能复用连接
|
||||
@@ -317,7 +317,7 @@
|
||||
[self waitForExpectations:@[expectation] timeout:35.0];
|
||||
}
|
||||
|
||||
// I.4 超时与活跃连接冲突(需要31秒,标记为慢测试)
|
||||
// I.4 超时与活跃连接冲突(需<EFBFBD><EFBFBD>?1秒,标记为慢测试<EFBFBD><EFBFBD>?
|
||||
- (void)testRaceCondition_ExpiredConnectionPruning_CreatesNewConnection {
|
||||
// 跳过此测试如果环境变量设置了 SKIP_SLOW_TESTS
|
||||
if (getenv("SKIP_SLOW_TESTS")) {
|
||||
@@ -335,7 +335,7 @@
|
||||
error:&error1];
|
||||
XCTAssertTrue(response1 != nil || error1 != nil);
|
||||
|
||||
// 等待超过30秒超时
|
||||
// 等待超过30秒超<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:31.0];
|
||||
|
||||
// 新请求应该创建新连接(旧连接已过期)
|
||||
@@ -352,19 +352,19 @@
|
||||
[self waitForExpectations:@[expectation] timeout:70.0];
|
||||
}
|
||||
|
||||
// I.5 错误恢复竞态
|
||||
// I.5 错误恢复竞<EFBFBD><EFBFBD>?
|
||||
- (void)testRaceCondition_ErrorRecovery_PoolRemainsHealthy {
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
// 发起一些会失败的请求
|
||||
// 发起一些会失败的请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 3; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Error %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
|
||||
dispatch_async(queue, ^{
|
||||
NSError *error = nil;
|
||||
// 使用短超时导致失败
|
||||
// 使用短超时导致失<EFBFBD><EFBFBD>?
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/5"
|
||||
userAgent:@"ErrorTest"
|
||||
timeout:1.0
|
||||
@@ -391,9 +391,9 @@
|
||||
[self waitForExpectations:@[recoveryExpectation] timeout:20.0];
|
||||
}
|
||||
|
||||
#pragma mark - N. 并发多端口测试
|
||||
#pragma mark - N. 并发多端口测<EFBFBD><EFBFBD>?
|
||||
|
||||
// N.1 并发保持连接(慢测试)
|
||||
// N.1 并发保持连接(慢测试<EFBFBD><EFBFBD>?
|
||||
- (void)testConcurrentMultiPort_ParallelKeepAlive_IndependentConnections {
|
||||
if (getenv("SKIP_SLOW_TESTS")) {
|
||||
return;
|
||||
@@ -402,7 +402,7 @@
|
||||
XCTestExpectation *expectation11443 = [self expectationWithDescription:@"Port 11443 keep-alive"];
|
||||
XCTestExpectation *expectation11444 = [self expectationWithDescription:@"Port 11444 keep-alive"];
|
||||
|
||||
// 线程 1:向端口 11443 发起 10 个请求,间隔 1 秒
|
||||
// 线程 1:向端口 11443 发起 10 个请求,间隔 1 <EFBFBD><EFBFBD>?
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
if (i > 0) {
|
||||
@@ -417,7 +417,7 @@
|
||||
[expectation11443 fulfill];
|
||||
});
|
||||
|
||||
// 线程 2:同时向端口 11444 发起 10 个请求,间隔 1 秒
|
||||
// 线程 2:同时向端口 11444 发起 10 个请求,间隔 1 <EFBFBD><EFBFBD>?
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
if (i > 0) {
|
||||
@@ -449,7 +449,7 @@
|
||||
portRequestCounts[port] = @0;
|
||||
}
|
||||
|
||||
// 以轮询方式向 4 个端口分发 100 个请求
|
||||
// 以轮询方式向 4 个端口分<EFBFBD><EFBFBD>?100 个请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < totalRequests; i++) {
|
||||
NSNumber *port = ports[i % ports.count];
|
||||
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
|
||||
@@ -466,7 +466,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 验证每个端口大约获得 25 个请求
|
||||
// 验证每个端口大约获得 25 个请<EFBFBD><EFBFBD>?
|
||||
for (NSNumber *port in ports) {
|
||||
NSInteger count = [portRequestCounts[port] integerValue];
|
||||
XCTAssertEqual(count, 25, @"Port %@ should receive 25 requests", port);
|
||||
@@ -478,12 +478,12 @@
|
||||
[self waitForExpectations:@[expectation] timeout:180.0];
|
||||
}
|
||||
|
||||
// N.3 混合负载多端口场景
|
||||
// N.3 混合负载多端口场<EFBFBD><EFBFBD>?
|
||||
- (void)testConcurrentMultiPort_MixedLoadPattern_RobustHandling {
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
// 端口 11443:高负载(20 个请求)
|
||||
// 端口 11443:高负载<EFBFBD><EFBFBD>?0 个请求)
|
||||
for (NSInteger i = 0; i < 20; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Heavy11443 %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
@@ -498,7 +498,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 端口 11444:中负载(10 个请求)
|
||||
// 端口 11444:中负载<EFBFBD><EFBFBD>?0 个请求)
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Medium11444 %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
@@ -513,7 +513,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 端口 11445:低负载(5 个请求)
|
||||
// 端口 11445:低负载<EFBFBD><EFBFBD>? 个请求)
|
||||
for (NSInteger i = 0; i < 5; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Light11445 %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClient_EdgeCasesAndTimeoutTests.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
// 边界条件与超时测试 - 包含边界条件 (M)、超时交互 (P) 和 Connection 头部 (R) 测试组
|
||||
// 测试总数:15 个(M:4 + P:6 + R:5)
|
||||
// 边界条件与超时测<EFBFBD><EFBFBD>?- 包含边界条件 (M)、超时交<EFBFBD><EFBFBD>?(P) <EFBFBD><EFBFBD>?Connection 头部 (R) 测试<EFBFBD><EFBFBD>?
|
||||
// 测试总数<EFBFBD><EFBFBD>?5 个(M:4 + P:6 + R:5<EFBFBD><EFBFBD>?
|
||||
//
|
||||
|
||||
#import "HttpdnsNWHTTPClientTestBase.h"
|
||||
@@ -17,14 +17,14 @@
|
||||
|
||||
@implementation HttpdnsNWHTTPClient_EdgeCasesAndTimeoutTests
|
||||
|
||||
#pragma mark - M. 边界条件与验证测试
|
||||
#pragma mark - M. 边界条件与验证测<EFBFBD><EFBFBD>?
|
||||
|
||||
// M.1 连接复用边界:端口内复用,端口间隔离
|
||||
- (void)testEdgeCase_ConnectionReuseWithinPortOnly_NotAcross {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Reuse boundaries"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 请求 A 到端口 11443
|
||||
// 请求 A 到端<EFBFBD><EFBFBD>?11443
|
||||
CFAbsoluteTime timeA = CFAbsoluteTimeGetCurrent();
|
||||
NSError *errorA = nil;
|
||||
HttpdnsNWHTTPClientResponse *responseA = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
@@ -34,7 +34,7 @@
|
||||
CFAbsoluteTime elapsedA = CFAbsoluteTimeGetCurrent() - timeA;
|
||||
XCTAssertNotNil(responseA);
|
||||
|
||||
// 请求 B 到端口 11443(应该复用连接,可能更快)
|
||||
// 请求 B 到端<EFBFBD><EFBFBD>?11443(应该复用连接,可能更快<EFBFBD><EFBFBD>?
|
||||
CFAbsoluteTime timeB = CFAbsoluteTimeGetCurrent();
|
||||
NSError *errorB = nil;
|
||||
HttpdnsNWHTTPClientResponse *responseB = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
@@ -44,7 +44,7 @@
|
||||
CFAbsoluteTime elapsedB = CFAbsoluteTimeGetCurrent() - timeB;
|
||||
XCTAssertNotNil(responseB);
|
||||
|
||||
// 请求 C 到端口 11444(应该创建新连接)
|
||||
// 请求 C 到端<EFBFBD><EFBFBD>?11444(应该创建新连接<EFBFBD><EFBFBD>?
|
||||
CFAbsoluteTime timeC = CFAbsoluteTimeGetCurrent();
|
||||
NSError *errorC = nil;
|
||||
HttpdnsNWHTTPClientResponse *responseC = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
|
||||
@@ -54,7 +54,7 @@
|
||||
CFAbsoluteTime elapsedC = CFAbsoluteTimeGetCurrent() - timeC;
|
||||
XCTAssertNotNil(responseC);
|
||||
|
||||
// 请求 D 到端口 11444(应该复用端口 11444 的连接)
|
||||
// 请求 D 到端<EFBFBD><EFBFBD>?11444(应该复用端<EFBFBD><EFBFBD>?11444 的连接)
|
||||
CFAbsoluteTime timeD = CFAbsoluteTimeGetCurrent();
|
||||
NSError *errorD = nil;
|
||||
HttpdnsNWHTTPClientResponse *responseD = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
|
||||
@@ -76,14 +76,14 @@
|
||||
[self waitForExpectations:@[expectation] timeout:70.0];
|
||||
}
|
||||
|
||||
// M.2 高端口数量压力测试
|
||||
// M.2 高端口数量压力测<EFBFBD><EFBFBD>?
|
||||
- (void)testEdgeCase_HighPortCount_AllPortsManaged {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"High port count"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445, @11446];
|
||||
|
||||
// 第一轮:向所有端口各发起一个请求
|
||||
// 第一轮:向所有端口各发起一个请<EFBFBD><EFBFBD>?
|
||||
for (NSNumber *port in ports) {
|
||||
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
|
||||
NSError *error = nil;
|
||||
@@ -94,7 +94,7 @@
|
||||
XCTAssertNotNil(response, @"First round request to port %@ should succeed", port);
|
||||
}
|
||||
|
||||
// 第二轮:再次向所有端口发起请求(应该复用连接)
|
||||
// 第二轮:再次向所有端口发起请求(应该复用连接<EFBFBD><EFBFBD>?
|
||||
for (NSNumber *port in ports) {
|
||||
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
|
||||
NSError *error = nil;
|
||||
@@ -111,7 +111,7 @@
|
||||
[self waitForExpectations:@[expectation] timeout:120.0];
|
||||
}
|
||||
|
||||
// M.3 并发池访问安全性
|
||||
// M.3 并发池访问安全<EFBFBD><EFBFBD>?
|
||||
- (void)testEdgeCase_ConcurrentPoolAccess_NoDataRace {
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
@@ -119,7 +119,7 @@
|
||||
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445];
|
||||
NSInteger requestsPerPort = 5;
|
||||
|
||||
// 向三个端口并发发起请求
|
||||
// 向三个端口并发发起请<EFBFBD><EFBFBD>?
|
||||
for (NSNumber *port in ports) {
|
||||
for (NSInteger i = 0; i < requestsPerPort; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Port %@ Req %ld", port, (long)i]];
|
||||
@@ -132,7 +132,7 @@
|
||||
userAgent:@"ConcurrentAccess"
|
||||
timeout:15.0
|
||||
error:&error];
|
||||
// 如果没有崩溃或断言失败,说明并发访问安全
|
||||
// 如果没有崩溃或断言失败,说明并发访问安<EFBFBD><EFBFBD>?
|
||||
XCTAssertTrue(response != nil || error != nil);
|
||||
[expectation fulfill];
|
||||
});
|
||||
@@ -160,7 +160,7 @@
|
||||
error:&error];
|
||||
}
|
||||
|
||||
// 阶段 2:切换到端口 11444,发起多个请求
|
||||
// 阶段 2:切换到端口 11444,发起多个请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 5; i++) {
|
||||
NSError *error = nil;
|
||||
[self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
|
||||
@@ -169,10 +169,10 @@
|
||||
error:&error];
|
||||
}
|
||||
|
||||
// 等待超过 30 秒,让端口 11443 的连接过期
|
||||
// 等待超过 30 秒,让端<EFBFBD><EFBFBD>?11443 的连接过<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:31.0];
|
||||
|
||||
// 阶段 3:验证端口 11444 仍然可用
|
||||
// 阶段 3:验证端<EFBFBD><EFBFBD>?11444 仍然可用
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
|
||||
userAgent:@"Port11444After"
|
||||
@@ -180,7 +180,7 @@
|
||||
error:&error1];
|
||||
XCTAssertNotNil(response1, @"Port 11444 should still work after 11443 expired");
|
||||
|
||||
// 阶段 4:端口 11443 应该创建新连接(旧连接已过期)
|
||||
// 阶段 4:端<EFBFBD><EFBFBD>?11443 应该创建新连接(旧连接已过期<EFBFBD><EFBFBD>?
|
||||
NSError *error2 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
userAgent:@"Port11443New"
|
||||
@@ -201,11 +201,11 @@
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 验证初始状态
|
||||
// 验证初始状<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 0,
|
||||
@"Pool should be empty initially");
|
||||
|
||||
// 发起超时请求(delay 10s, timeout 1s)
|
||||
// 发起超时请求(delay 10s, timeout 1s<EFBFBD><EFBFBD>?
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
|
||||
userAgent:@"TimeoutTest"
|
||||
@@ -219,7 +219,7 @@
|
||||
// 等待异步 returnConnection 完成
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证池状态:超时连接应该被移除
|
||||
// 验证池状态:超时连接应该被移<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 0,
|
||||
@"Timed-out connection should be removed from pool");
|
||||
XCTAssertEqual([self.client totalConnectionCount], 0,
|
||||
@@ -261,7 +261,7 @@
|
||||
// 等待 returnConnection
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证池恢复:现在应该有 1 个连接(来自第二个请求)
|
||||
// 验证池恢复:现在应该<EFBFBD><EFBFBD>?1 个连接(来自第二个请求)
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
|
||||
@"Pool should have 1 connection from second request");
|
||||
|
||||
@@ -274,14 +274,14 @@
|
||||
XCTAssertNotNil(response3);
|
||||
XCTAssertEqual(response3.statusCode, 200);
|
||||
|
||||
// 验证统计:1 个超时(创建后移除)+ 1 个成功(创建)+ 1 个复用
|
||||
// 验证统计<EFBFBD><EFBFBD>? 个超时(创建后移除)+ 1 个成功(创建<EFBFBD><EFBFBD>? 1 个复<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 2,
|
||||
@"Should have created 2 connections (1 timed out, 1 succeeded)");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 1,
|
||||
@"Third request should reuse second's connection");
|
||||
}
|
||||
|
||||
// P.3 并发场景:部分超时不影响成功请求的连接复用
|
||||
// P.3 并发场景:部分超时不影响成功请求的连接复<EFBFBD><EFBFBD>?
|
||||
- (void)testTimeout_ConcurrentPartialTimeout_SuccessfulRequestsReuse {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
@@ -294,7 +294,7 @@
|
||||
__block NSInteger successCount = 0;
|
||||
__block NSInteger timeoutCount = 0;
|
||||
|
||||
// 发起 10 个请求:5 个正常,5 个超时
|
||||
// 发起 10 个请求:5 个正常,5 个超<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Request %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
@@ -305,11 +305,11 @@
|
||||
NSTimeInterval timeout;
|
||||
|
||||
if (i % 2 == 0) {
|
||||
// 偶数:正常请求
|
||||
// 偶数:正常请<EFBFBD><EFBFBD>?
|
||||
urlString = @"http://127.0.0.1:11080/get";
|
||||
timeout = 15.0;
|
||||
} else {
|
||||
// 奇数:超时请求
|
||||
// 奇数:超时请<EFBFBD><EFBFBD>?
|
||||
urlString = @"http://127.0.0.1:11080/delay/10";
|
||||
timeout = 0.5;
|
||||
}
|
||||
@@ -339,7 +339,7 @@
|
||||
XCTAssertEqual(successCount, 5, @"5 requests should succeed");
|
||||
XCTAssertEqual(timeoutCount, 5, @"5 requests should timeout");
|
||||
|
||||
// 验证连接创建数合理(5个成功 + 5个超时 = 最多10个,可能有复用)
|
||||
// 验证连接创建数合理(5个成<EFBFBD><EFBFBD>?+ 5个超<EFBFBD><EFBFBD>?= 最<EFBFBD><EFBFBD>?0个,可能有复用)
|
||||
XCTAssertGreaterThan(self.client.connectionCreationCount, 0,
|
||||
@"Should have created connections for concurrent requests");
|
||||
XCTAssertLessThanOrEqual(self.client.connectionCreationCount, 10,
|
||||
@@ -348,8 +348,8 @@
|
||||
// 等待所有连接归还(异步操作需要更长时间)
|
||||
[NSThread sleepForTimeInterval:2.0];
|
||||
|
||||
// 验证总连接数合理(无泄漏)- 关键验证点
|
||||
// 在并发场景下,成功的连接可能已经被关闭(remote close),池可能为空
|
||||
// 验证总连接数合理(无泄漏<EFBFBD><EFBFBD>? 关键验证<EFBFBD><EFBFBD>?
|
||||
// 在并发场景下,成功的连接可能已经被关闭(remote close),池可能为<EFBFBD><EFBFBD>?
|
||||
XCTAssertLessThanOrEqual([self.client totalConnectionCount], 4,
|
||||
@"Total connections should not exceed pool limit (no leak)");
|
||||
|
||||
@@ -363,12 +363,12 @@
|
||||
XCTAssertEqual(recoveryResponse.statusCode, 200, @"Recovery request should succeed");
|
||||
}
|
||||
|
||||
// P.4 连续超时不导致连接泄漏
|
||||
// P.4 连续超时不导致连接泄<EFBFBD><EFBFBD>?
|
||||
- (void)testTimeout_ConsecutiveTimeouts_NoConnectionLeak {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 连续发起 10 个超时请求
|
||||
// 连续发起 10 个超时请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
|
||||
@@ -381,13 +381,13 @@
|
||||
[NSThread sleepForTimeInterval:0.2];
|
||||
}
|
||||
|
||||
// 验证池状态:无连接泄漏
|
||||
// 验证池状态:无连接泄<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 0,
|
||||
@"Pool should be empty after consecutive timeouts");
|
||||
XCTAssertEqual([self.client totalConnectionCount], 0,
|
||||
@"No connections should leak");
|
||||
|
||||
// 验证统计:每次都创建新连接(因为超时的被移除)
|
||||
// 验证统计:每次都创建新连接(因为超时的被移除<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 10,
|
||||
@"Should have created 10 connections (all timed out and removed)");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 0,
|
||||
@@ -403,7 +403,7 @@
|
||||
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
// 请求 A:超时(delay 10s, timeout 2s)
|
||||
// 请求 A:超时(delay 10s, timeout 2s<EFBFBD><EFBFBD>?
|
||||
dispatch_async(queue, ^{
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
|
||||
@@ -414,9 +414,9 @@
|
||||
[timeoutExpectation fulfill];
|
||||
});
|
||||
|
||||
// 请求 B:正常(应该不受 A 阻塞)
|
||||
// 请求 B:正常(应该不受 A 阻塞<EFBFBD><EFBFBD>?
|
||||
dispatch_async(queue, ^{
|
||||
// 稍微延迟,确保 A 先开始
|
||||
// 稍微延迟,确<EFBFBD><EFBFBD>?A 先开<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:0.1];
|
||||
|
||||
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
|
||||
@@ -430,7 +430,7 @@
|
||||
XCTAssertNotNil(response, @"Request B should succeed despite A timing out");
|
||||
XCTAssertEqual(response.statusCode, 200);
|
||||
|
||||
// 验证请求 B 没有被请求 A 阻塞(应该很快完成)
|
||||
// 验证请求 B 没有被请<EFBFBD><EFBFBD>?A 阻塞(应该很快完成)
|
||||
XCTAssertLessThan(elapsed, 5.0,
|
||||
@"Request B should complete quickly, not blocked by A's timeout");
|
||||
|
||||
@@ -442,20 +442,20 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证池状态:只有请求 B 的连接
|
||||
// 验证池状态:只有请求 B 的连<EFBFBD><EFBFBD>?
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
|
||||
@"Pool should have 1 connection from successful request B");
|
||||
}
|
||||
|
||||
// P.6 多端口场景下的超时隔离
|
||||
// P.6 多端口场景下的超时隔<EFBFBD><EFBFBD>?
|
||||
- (void)testTimeout_MultiPort_IsolatedPoolCleaning {
|
||||
[self.client resetPoolStatistics];
|
||||
|
||||
NSString *poolKey11443 = @"127.0.0.1:11443:tls";
|
||||
NSString *poolKey11444 = @"127.0.0.1:11444:tls";
|
||||
|
||||
// 端口 11443:超时请求
|
||||
// 端口 11443:超时请<EFBFBD><EFBFBD>?
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/delay/10"
|
||||
userAgent:@"Port11443Timeout"
|
||||
@@ -463,7 +463,7 @@
|
||||
error:&error1];
|
||||
XCTAssertNil(response1, @"Port 11443 request should timeout");
|
||||
|
||||
// 端口 11444:正常请求
|
||||
// 端口 11444:正常请<EFBFBD><EFBFBD>?
|
||||
NSError *error2 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
|
||||
userAgent:@"Port11444Success"
|
||||
@@ -475,7 +475,7 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证端口隔离:端口 11443 无连接,端口 11444 有 1 个连接
|
||||
// 验证端口隔离:端<EFBFBD><EFBFBD>?11443 无连接,端口 11444 <EFBFBD><EFBFBD>?1 个连<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey11443], 0,
|
||||
@"Port 11443 pool should be empty (timed out)");
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey11444], 1,
|
||||
@@ -485,7 +485,7 @@
|
||||
XCTAssertEqual([self.client totalConnectionCount], 1,
|
||||
@"Total should be 1 (only from port 11444)");
|
||||
|
||||
// 再次请求端口 11444:应该复用连接
|
||||
// 再次请求端口 11444:应该复用连<EFBFBD><EFBFBD>?
|
||||
NSError *error3 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response3 = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
|
||||
userAgent:@"Port11444Reuse"
|
||||
@@ -501,7 +501,7 @@
|
||||
|
||||
#pragma mark - R. Connection 头部处理测试
|
||||
|
||||
// R.1 Connection: close 导致连接被立即失效
|
||||
// R.1 Connection: close 导致连接被立即失<EFBFBD><EFBFBD>?
|
||||
- (void)testConnectionHeader_Close_ConnectionInvalidated {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
@@ -522,7 +522,7 @@
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 0,
|
||||
@"Connection with 'Connection: close' header should be invalidated and not returned to pool");
|
||||
|
||||
// 请求 2:应该创建新连接(第一个连接已失效)
|
||||
// 请求 2:应该创建新连接(第一个连接已失效<EFBFBD><EFBFBD>?
|
||||
NSError *error2 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/connection-test?mode=keep-alive"
|
||||
userAgent:@"KeepAliveTest"
|
||||
@@ -531,7 +531,7 @@
|
||||
XCTAssertNotNil(response2);
|
||||
XCTAssertEqual(response2.statusCode, 200);
|
||||
|
||||
// 验证统计:创建了 2 个连接(第一个被 close,第二个正常)
|
||||
// 验证统计:创建了 2 个连接(第一个被 close,第二个正常<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 2,
|
||||
@"Should have created 2 connections (first closed by server)");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 0,
|
||||
@@ -559,7 +559,7 @@
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
|
||||
@"Connection with 'Connection: keep-alive' should be returned to pool");
|
||||
|
||||
// 请求 2:应该复用第一个连接
|
||||
// 请求 2:应该复用第一个连<EFBFBD><EFBFBD>?
|
||||
NSError *error2 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/connection-test?mode=keep-alive"
|
||||
userAgent:@"KeepAlive2"
|
||||
@@ -568,7 +568,7 @@
|
||||
XCTAssertNotNil(response2);
|
||||
XCTAssertEqual(response2.statusCode, 200);
|
||||
|
||||
// 验证统计:只创建了 1 个连接,第二个请求复用了它
|
||||
// 验证统计:只创建<EFBFBD><EFBFBD>?1 个连接,第二个请求复用了<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 1,
|
||||
@"Should have created only 1 connection");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 1,
|
||||
@@ -592,7 +592,7 @@
|
||||
// 等待异步 returnConnection 完成
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证:连接不在池中(Proxy-Connection: close 应该失效连接)
|
||||
// 验证:连接不在池中(Proxy-Connection: close 应该失效连接<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 0,
|
||||
@"Connection with 'Proxy-Connection: close' header should be invalidated");
|
||||
|
||||
@@ -605,7 +605,7 @@
|
||||
XCTAssertNotNil(response2);
|
||||
XCTAssertEqual(response2.statusCode, 200);
|
||||
|
||||
// 验证统计:创建了 2 个连接
|
||||
// 验证统计:创建了 2 个连<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 2,
|
||||
@"Should have created 2 connections (first closed by proxy header)");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 0,
|
||||
@@ -617,7 +617,7 @@
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 测试 1:CONNECTION: CLOSE (全大写)
|
||||
// 测试 1:CONNECTION: CLOSE (全大<EFBFBD><EFBFBD>?
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/connection-test?mode=close-uppercase"
|
||||
userAgent:@"UppercaseTest"
|
||||
@@ -628,11 +628,11 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证:连接不在池中(大写 CLOSE 也应生效)
|
||||
// 验证:连接不在池中(大写 CLOSE 也应生效<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 0,
|
||||
@"'CONNECTION: CLOSE' (uppercase) should also close connection");
|
||||
|
||||
// 测试 2:Connection: Close (混合大小写)
|
||||
// 测试 2:Connection: Close (混合大小<EFBFBD><EFBFBD>?
|
||||
NSError *error2 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/connection-test?mode=close-mixed"
|
||||
userAgent:@"MixedCaseTest"
|
||||
@@ -654,7 +654,7 @@
|
||||
@"No reuse for any closed connections");
|
||||
}
|
||||
|
||||
// R.5 并发场景:混合 close 和 keep-alive
|
||||
// R.5 并发场景:混<EFBFBD><EFBFBD>?close <EFBFBD><EFBFBD>?keep-alive
|
||||
- (void)testConnectionHeader_ConcurrentMixed_CloseAndKeepAliveIsolated {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
@@ -667,7 +667,7 @@
|
||||
__block NSInteger closeCount = 0;
|
||||
__block NSInteger keepAliveCount = 0;
|
||||
|
||||
// 发起 10 个请求:5 个 close,5 个 keep-alive
|
||||
// 发起 10 个请求:5 <EFBFBD><EFBFBD>?close<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?keep-alive
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Request %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
@@ -711,23 +711,23 @@
|
||||
XCTAssertEqual(closeCount, 5, @"5 close requests should succeed");
|
||||
XCTAssertEqual(keepAliveCount, 5, @"5 keep-alive requests should succeed");
|
||||
|
||||
// 等待所有连接归还
|
||||
// 等待所有连接归<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证池状态:
|
||||
// - close 连接全部被失效(不在池中)
|
||||
// - keep-alive 连接可能在池中(取决于并发时序和 remote close)
|
||||
// - 关键是:总数不超过 4(池限制),无泄漏
|
||||
// - close 连接全部被失效(不在池中<EFBFBD><EFBFBD>?
|
||||
// - keep-alive 连接可能在池中(取决于并发时序和 remote close<EFBFBD><EFBFBD>?
|
||||
// - 关键是:总数不超<EFBFBD><EFBFBD>?4(池限制),无泄<EFBFBD><EFBFBD>?
|
||||
NSInteger poolSize = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertLessThanOrEqual(poolSize, 4,
|
||||
@"Pool size should not exceed limit (no leak from close connections)");
|
||||
|
||||
// 验证统计:close 连接不应该被复用
|
||||
// 创建数应该 >= 6 (至少 5 个 close + 1 个 keep-alive,因为 close 不能复用)
|
||||
// 创建数应<EFBFBD><EFBFBD>?>= 6 (至少 5 <EFBFBD><EFBFBD>?close + 1 <EFBFBD><EFBFBD>?keep-alive,因<EFBFBD><EFBFBD>?close 不能复用)
|
||||
XCTAssertGreaterThanOrEqual(self.client.connectionCreationCount, 6,
|
||||
@"Should have created at least 6 connections (5 close + at least 1 keep-alive)");
|
||||
|
||||
// 后续请求验证池仍然健康
|
||||
// 后续请求验证池仍然健<EFBFBD><EFBFBD>?
|
||||
NSError *recoveryError = nil;
|
||||
HttpdnsNWHTTPClientResponse *recoveryResponse = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"RecoveryTest"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClient_PoolManagementTests.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
// 连接池管理测试 - 包含多端口隔离 (K)、端口池耗尽 (L)、池验证 (O)、空闲超时 (S) 测试组
|
||||
// 测试总数:16 个(K:5 + L:3 + O:3 + S:5)
|
||||
// 连接池管理测<EFBFBD><EFBFBD>?- 包含多端口隔<EFBFBD><EFBFBD>?(K)、端口池耗尽 (L)、池验证 (O)、空闲超<EFBFBD><EFBFBD>?(S) 测试<EFBFBD><EFBFBD>?
|
||||
// 测试总数<EFBFBD><EFBFBD>?6 个(K:5 + L:3 + O:3 + S:5<EFBFBD><EFBFBD>?
|
||||
//
|
||||
|
||||
#import "HttpdnsNWHTTPClientTestBase.h"
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
@implementation HttpdnsNWHTTPClient_PoolManagementTests
|
||||
|
||||
#pragma mark - K. 多端口连接隔离测试
|
||||
#pragma mark - K. 多端口连接隔离测<EFBFBD><EFBFBD>?
|
||||
|
||||
// K.1 不同 HTTPS 端口使用不同连接池
|
||||
// K.1 不同 HTTPS 端口使用不同连接<EFBFBD><EFBFBD>?
|
||||
- (void)testMultiPort_DifferentHTTPSPorts_SeparatePoolKeys {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Different ports use different pools"];
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
XCTAssertNotNil(response2, @"First request to port 11444 should succeed");
|
||||
XCTAssertEqual(response2.statusCode, 200);
|
||||
|
||||
// 再次请求端口 11443(应该复用之前的连接)
|
||||
// 再次请求端口 11443(应该复用之前的连接<EFBFBD><EFBFBD>?
|
||||
NSError *error3 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response3 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
userAgent:@"Port11443Again"
|
||||
@@ -57,7 +57,7 @@
|
||||
[self waitForExpectations:@[expectation] timeout:50.0];
|
||||
}
|
||||
|
||||
// K.2 三个不同 HTTPS 端口的并发请求
|
||||
// K.2 三个不同 HTTPS 端口的并发请<EFBFBD><EFBFBD>?
|
||||
- (void)testMultiPort_ConcurrentThreePorts_AllSucceed {
|
||||
NSInteger requestsPerPort = 10;
|
||||
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445];
|
||||
@@ -97,17 +97,17 @@
|
||||
XCTAssertEqual(successCount, ports.count * requestsPerPort, @"All 30 requests should succeed");
|
||||
}
|
||||
|
||||
// K.3 快速切换端口模式
|
||||
// K.3 快速切换端口模<EFBFBD><EFBFBD>?
|
||||
- (void)testMultiPort_SequentialPortSwitching_ConnectionReusePerPort {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Sequential port switching"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSArray<NSString *> *urls = @[
|
||||
@"https://127.0.0.1:11443/get", // 第一次访问 11443
|
||||
@"https://127.0.0.1:11444/get", // 第一次访问 11444
|
||||
@"https://127.0.0.1:11445/get", // 第一次访问 11445
|
||||
@"https://127.0.0.1:11443/get", // 第二次访问 11443(应复用)
|
||||
@"https://127.0.0.1:11444/get", // 第二次访问 11444(应复用)
|
||||
@"https://127.0.0.1:11443/get", // 第一次访<EFBFBD><EFBFBD>?11443
|
||||
@"https://127.0.0.1:11444/get", // 第一次访<EFBFBD><EFBFBD>?11444
|
||||
@"https://127.0.0.1:11445/get", // 第一次访<EFBFBD><EFBFBD>?11445
|
||||
@"https://127.0.0.1:11443/get", // 第二次访<EFBFBD><EFBFBD>?11443(应复用<EFBFBD><EFBFBD>?
|
||||
@"https://127.0.0.1:11444/get", // 第二次访<EFBFBD><EFBFBD>?11444(应复用<EFBFBD><EFBFBD>?
|
||||
];
|
||||
|
||||
NSMutableArray<NSNumber *> *responseTimes = [NSMutableArray array];
|
||||
@@ -140,7 +140,7 @@
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Per-port pool limits"];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 向端口 11443 发送 10 个请求
|
||||
// 向端<EFBFBD><EFBFBD>?11443 发<EFBFBD><EFBFBD>?10 个请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
@@ -150,7 +150,7 @@
|
||||
XCTAssertTrue(response != nil || error != nil);
|
||||
}
|
||||
|
||||
// 向端口 11444 发送 10 个请求(应该有独立的池)
|
||||
// 向端<EFBFBD><EFBFBD>?11444 发<EFBFBD><EFBFBD>?10 个请求(应该有独立的池)
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
|
||||
@@ -163,7 +163,7 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证两个端口都仍然可用
|
||||
// 验证两个端口都仍然可<EFBFBD><EFBFBD>?
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
userAgent:@"Verify11443"
|
||||
@@ -193,7 +193,7 @@
|
||||
NSInteger totalRequests = 20;
|
||||
NSInteger successCount = 0;
|
||||
|
||||
// 交错请求:依次循环访问所有端口
|
||||
// 交错请求:依次循环访问所有端<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < totalRequests; i++) {
|
||||
NSNumber *port = ports[i % ports.count];
|
||||
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
|
||||
@@ -219,7 +219,7 @@
|
||||
|
||||
#pragma mark - L. 基于端口的池耗尽测试
|
||||
|
||||
// L.1 四个端口同时承载高负载
|
||||
// L.1 四个端口同时承载高负<EFBFBD><EFBFBD>?
|
||||
- (void)testPoolExhaustion_FourPortsSimultaneous_AllSucceed {
|
||||
NSInteger requestsPerPort = 10;
|
||||
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445, @11446];
|
||||
@@ -229,7 +229,7 @@
|
||||
NSLock *successCountLock = [[NSLock alloc] init];
|
||||
__block NSInteger successCount = 0;
|
||||
|
||||
// 向 4 个端口各发起 10 个并发请求(共 40 个)
|
||||
// <EFBFBD><EFBFBD>?4 个端口各发起 10 个并发请求(<EFBFBD><EFBFBD>?40 个)
|
||||
for (NSNumber *port in ports) {
|
||||
for (NSInteger i = 0; i < requestsPerPort; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Port %@ Request %ld", port, (long)i]];
|
||||
@@ -256,11 +256,11 @@
|
||||
|
||||
[self waitForExpectations:expectations timeout:80.0];
|
||||
|
||||
// 验证成功率 > 95%(允许少量因并发导致的失败)
|
||||
// 验证成功<EFBFBD><EFBFBD>?> 95%(允许少量因并发导致的失败)
|
||||
XCTAssertGreaterThan(successCount, 38, @"At least 95%% of 40 requests should succeed");
|
||||
}
|
||||
|
||||
// L.2 单个端口耗尽时其他端口不受影响
|
||||
// L.2 单个端口耗尽时其他端口不受影<EFBFBD><EFBFBD>?
|
||||
- (void)testPoolExhaustion_SinglePortExhausted_OthersUnaffected {
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
@@ -268,7 +268,7 @@
|
||||
__block NSInteger port11444SuccessCount = 0;
|
||||
NSLock *countLock = [[NSLock alloc] init];
|
||||
|
||||
// 向端口 11443 发起 20 个并发请求(可能导致池耗尽)
|
||||
// 向端<EFBFBD><EFBFBD>?11443 发起 20 个并发请求(可能导致池耗尽<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 20; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Exhaust11443 %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
@@ -283,7 +283,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 同时向端口 11444 发起 5 个请求(应该不受 11443 影响)
|
||||
// 同时向端<EFBFBD><EFBFBD>?11444 发起 5 个请求(应该不受 11443 影响<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 5; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Port11444 %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
@@ -316,7 +316,7 @@
|
||||
XCTAssertEqual(port11444SuccessCount, 5, @"All port 11444 requests should succeed despite 11443 load");
|
||||
}
|
||||
|
||||
// L.3 多端口使用后的连接清理
|
||||
// L.3 多端口使用后的连接清<EFBFBD><EFBFBD>?
|
||||
- (void)testPoolExhaustion_MultiPortCleanup_ExpiredConnectionsPruned {
|
||||
if (getenv("SKIP_SLOW_TESTS")) {
|
||||
return;
|
||||
@@ -327,7 +327,7 @@
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSArray<NSNumber *> *ports = @[@11443, @11444, @11445];
|
||||
|
||||
// 向三个端口各发起一个请求
|
||||
// 向三个端口各发起一个请<EFBFBD><EFBFBD>?
|
||||
for (NSNumber *port in ports) {
|
||||
NSString *urlString = [NSString stringWithFormat:@"https://127.0.0.1:%@/get", port];
|
||||
NSError *error = nil;
|
||||
@@ -338,7 +338,7 @@
|
||||
XCTAssertTrue(response != nil || error != nil);
|
||||
}
|
||||
|
||||
// 等待超过 30 秒,让所有连接过期
|
||||
// 等待超过 30 秒,让所有连接过<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:31.0];
|
||||
|
||||
// 再次向三个端口发起请求(应该创建新连接)
|
||||
@@ -358,14 +358,14 @@
|
||||
[self waitForExpectations:@[expectation] timeout:80.0];
|
||||
}
|
||||
|
||||
#pragma mark - O. 连接池验证测试(使用新增的检查 API)
|
||||
#pragma mark - O. 连接池验证测试(使用新增的检<EFBFBD><EFBFBD>?API<EFBFBD><EFBFBD>?
|
||||
|
||||
// O.1 综合连接池验证 - 演示所有检查能力
|
||||
// O.1 综合连接池验<EFBFBD><EFBFBD>?- 演示所有检查能<EFBFBD><EFBFBD>?
|
||||
- (void)testPoolVerification_ComprehensiveCheck_AllAspectsVerified {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 初始状态:无连接
|
||||
// 初始状态:无连<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 0,
|
||||
@"Pool should be empty initially");
|
||||
XCTAssertEqual([self.client totalConnectionCount], 0,
|
||||
@@ -375,7 +375,7 @@
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 0,
|
||||
@"Reuse count should be 0 initially");
|
||||
|
||||
// 发送 5 个请求到同一端点
|
||||
// 发<EFBFBD><EFBFBD>?5 个请求到同一端点
|
||||
for (NSInteger i = 0; i < 5; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
@@ -386,7 +386,7 @@
|
||||
XCTAssertEqual(response.statusCode, 200, @"Request %ld should return 200", (long)i);
|
||||
}
|
||||
|
||||
// 验证连接池状态
|
||||
// 验证连接池状<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
|
||||
@"Should have exactly 1 connection in pool for key: %@", poolKey);
|
||||
XCTAssertEqual([self.client totalConnectionCount], 1,
|
||||
@@ -398,7 +398,7 @@
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 4,
|
||||
@"Should reuse connection 4 times");
|
||||
|
||||
// 验证连接复用率
|
||||
// 验证连接复用<EFBFBD><EFBFBD>?
|
||||
CGFloat reuseRate = (CGFloat)self.client.connectionReuseCount /
|
||||
(self.client.connectionCreationCount + self.client.connectionReuseCount);
|
||||
XCTAssertGreaterThanOrEqual(reuseRate, 0.8,
|
||||
@@ -417,11 +417,11 @@
|
||||
NSString *key11443 = @"127.0.0.1:11443:tls";
|
||||
NSString *key11444 = @"127.0.0.1:11444:tls";
|
||||
|
||||
// 初始:两个池都为空
|
||||
// 初始:两个池都为<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:key11443], 0);
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:key11444], 0);
|
||||
|
||||
// 向端口 11443 发送 3 个请求
|
||||
// 向端<EFBFBD><EFBFBD>?11443 发<EFBFBD><EFBFBD>?3 个请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 3; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
@@ -431,13 +431,13 @@
|
||||
XCTAssertNotNil(response);
|
||||
}
|
||||
|
||||
// 验证端口 11443 的池状态
|
||||
// 验证端口 11443 的池状<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:key11443], 1,
|
||||
@"Port 11443 should have 1 connection");
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:key11444], 0,
|
||||
@"Port 11444 should still be empty");
|
||||
|
||||
// 向端口 11444 发送 3 个请求
|
||||
// 向端<EFBFBD><EFBFBD>?11444 发<EFBFBD><EFBFBD>?3 个请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 3; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"https://127.0.0.1:11444/get"
|
||||
@@ -455,7 +455,7 @@
|
||||
XCTAssertEqual([self.client totalConnectionCount], 2,
|
||||
@"Total should be 2 connections (one per port)");
|
||||
|
||||
// 验证统计:应该创建了 2 个连接,复用了 4 次
|
||||
// 验证统计:应该创建了 2 个连接,复用<EFBFBD><EFBFBD>?4 <EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 2,
|
||||
@"Should create 2 connections (one per port)");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 4,
|
||||
@@ -468,12 +468,12 @@
|
||||
XCTAssertTrue([allKeys containsObject:key11444], @"Should contain key for port 11444");
|
||||
}
|
||||
|
||||
// O.3 连接池容量限制验证
|
||||
// O.3 连接池容量限制验<EFBFBD><EFBFBD>?
|
||||
- (void)testPoolVerification_PoolCapacity_MaxFourConnections {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 发送 10 个连续请求(每个请求都会归还连接到池)
|
||||
// 发<EFBFBD><EFBFBD>?10 个连续请求(每个请求都会归还连接到池<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
@@ -486,12 +486,12 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证池大小不超过 4(kHttpdnsNWHTTPClientMaxIdleConnectionsPerKey)
|
||||
// 验证池大小不超过 4(kHttpdnsNWHTTPClientMaxIdleConnectionsPerKey<EFBFBD><EFBFBD>?
|
||||
NSUInteger poolSize = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertLessThanOrEqual(poolSize, 4,
|
||||
@"Pool size should not exceed 4 (actual: %lu)", (unsigned long)poolSize);
|
||||
|
||||
// 验证统计:应该只创建了 1 个连接(因为串行请求,每次都复用)
|
||||
// 验证统计:应该只创建<EFBFBD><EFBFBD>?1 个连接(因为串行请求,每次都复用<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 1,
|
||||
@"Should create only 1 connection for sequential requests");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 9,
|
||||
@@ -500,12 +500,12 @@
|
||||
|
||||
#pragma mark - S. 空闲超时详细测试
|
||||
|
||||
// S.1 混合过期和有效连接 - 选择性清理
|
||||
// S.1 混合过期和有效连<EFBFBD><EFBFBD>?- 选择性清<EFBFBD><EFBFBD>?
|
||||
- (void)testIdleTimeout_MixedExpiredValid_SelectivePruning {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 创建第一个连接
|
||||
// 创建第一个连<EFBFBD><EFBFBD>?
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"ConnectionA"
|
||||
@@ -517,7 +517,7 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 使用 DEBUG API 获取连接 A 并设置为过期(35 秒前)
|
||||
// 使用 DEBUG API 获取连接 A 并设置为过期<EFBFBD><EFBFBD>?5 秒前<EFBFBD><EFBFBD>?
|
||||
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
|
||||
XCTAssertEqual(connections.count, 1, @"Should have 1 connection in pool");
|
||||
|
||||
@@ -525,7 +525,7 @@
|
||||
NSDate *expiredDate = [NSDate dateWithTimeIntervalSinceNow:-35.0];
|
||||
[connectionA debugSetLastUsedDate:expiredDate];
|
||||
|
||||
// 创建第二个连接(通过并发请求)
|
||||
// 创建第二个连接(通过并发请求<EFBFBD><EFBFBD>?
|
||||
NSError *error2 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"ConnectionB"
|
||||
@@ -536,12 +536,12 @@
|
||||
// 等待归还
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证:应该有 1 个连接(connectionA 过期被移除,connectionB 留下)
|
||||
// 验证:应该有 1 个连接(connectionA 过期被移除,connectionB 留下<EFBFBD><EFBFBD>?
|
||||
connections = [self.client connectionsInPoolForKey:poolKey];
|
||||
XCTAssertEqual(connections.count, 1,
|
||||
@"Should have only 1 connection (expired A removed, valid B kept)");
|
||||
|
||||
// 第三个请求应该复用 connectionB
|
||||
// 第三个请求应该复<EFBFBD><EFBFBD>?connectionB
|
||||
[self.client resetPoolStatistics];
|
||||
NSError *error3 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response3 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
@@ -550,7 +550,7 @@
|
||||
error:&error3];
|
||||
XCTAssertNotNil(response3);
|
||||
|
||||
// 验证:复用了 connectionB(没有创建新连接)
|
||||
// 验证:复用了 connectionB(没有创建新连接<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 0,
|
||||
@"Should not create new connection (reuse existing valid connection)");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 1,
|
||||
@@ -562,7 +562,7 @@
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 创建第一个连接
|
||||
// 创建第一个连<EFBFBD><EFBFBD>?
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"Initial"
|
||||
@@ -572,17 +572,17 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 借出连接并保持 inUse=YES
|
||||
// 借出连接并保<EFBFBD><EFBFBD>?inUse=YES
|
||||
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
|
||||
XCTAssertEqual(connections.count, 1);
|
||||
|
||||
HttpdnsNWReusableConnection *conn = connections.firstObject;
|
||||
|
||||
// 手动设置为 60 秒前(远超 30 秒超时)
|
||||
// 手动设置<EFBFBD><EFBFBD>?60 秒前(远<EFBFBD><EFBFBD>?30 秒超时)
|
||||
NSDate *veryOldDate = [NSDate dateWithTimeIntervalSinceNow:-60.0];
|
||||
[conn debugSetLastUsedDate:veryOldDate];
|
||||
|
||||
// 将连接标记为使用中
|
||||
// 将连接标记为使用<EFBFBD><EFBFBD>?
|
||||
[conn debugSetInUse:YES];
|
||||
|
||||
// 触发清理(通过发起另一个并发请求)
|
||||
@@ -593,7 +593,7 @@
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
connectionsBefore = [self.client totalConnectionCount];
|
||||
|
||||
// 发起请求(会触发 pruneConnectionPool)
|
||||
// 发起请求(会触发 pruneConnectionPool<EFBFBD><EFBFBD>?
|
||||
NSError *error2 = nil;
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"TriggerPrune"
|
||||
@@ -608,18 +608,18 @@
|
||||
|
||||
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC));
|
||||
|
||||
// 清理:重置 inUse 状态
|
||||
// 清理:重<EFBFBD><EFBFBD>?inUse 状<EFBFBD><EFBFBD>?
|
||||
[conn debugSetInUse:NO];
|
||||
|
||||
// 验证:inUse=YES 的连接不应该被清理
|
||||
// connectionsBefore = 1 (旧连接), connectionsAfter = 2 (旧连接 + 新连接)
|
||||
// 验证:inUse=YES 的连接不应该被清<EFBFBD><EFBFBD>?
|
||||
// connectionsBefore = 1 (旧连<EFBFBD><EFBFBD>?, connectionsAfter = 2 (旧连<EFBFBD><EFBFBD>?+ 新连<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(connectionsBefore, 1,
|
||||
@"Should have 1 connection before (in-use protected)");
|
||||
XCTAssertEqual(connectionsAfter, 2,
|
||||
@"Should have 2 connections after (in-use connection NOT pruned, new connection added)");
|
||||
}
|
||||
|
||||
// S.3 所有连接过期 - 批量清理
|
||||
// S.3 所有连接过<EFBFBD><EFBFBD>?- 批量清理
|
||||
- (void)testIdleTimeout_AllExpired_BulkPruning {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
@@ -645,21 +645,21 @@
|
||||
|
||||
[self waitForExpectations:expectations timeout:30.0];
|
||||
|
||||
// 等待所有连接归还
|
||||
// 等待所有连接归<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证池已满
|
||||
// 验证池已<EFBFBD><EFBFBD>?
|
||||
NSUInteger poolSizeBefore = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertGreaterThan(poolSizeBefore, 0, @"Pool should have connections");
|
||||
|
||||
// 将所有连接设置为过期(31 秒前)
|
||||
// 将所有连接设置为过期<EFBFBD><EFBFBD>?1 秒前<EFBFBD><EFBFBD>?
|
||||
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
|
||||
NSDate *expiredDate = [NSDate dateWithTimeIntervalSinceNow:-31.0];
|
||||
for (HttpdnsNWReusableConnection *conn in connections) {
|
||||
[conn debugSetLastUsedDate:expiredDate];
|
||||
}
|
||||
|
||||
// 发起新请求(触发批量清理)
|
||||
// 发起新请求(触发批量清理<EFBFBD><EFBFBD>?
|
||||
NSError *errorNew = nil;
|
||||
HttpdnsNWHTTPClientResponse *responseNew = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"AfterBulkExpiry"
|
||||
@@ -677,7 +677,7 @@
|
||||
@"Pool should have only 1 connection (new one after bulk pruning)");
|
||||
}
|
||||
|
||||
// S.4 过期后池状态验证
|
||||
// S.4 过期后池状态验<EFBFBD><EFBFBD>?
|
||||
- (void)testIdleTimeout_PoolStateAfterExpiry_DirectVerification {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
@@ -692,11 +692,11 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证连接在池中
|
||||
// 验证连接在池<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
|
||||
@"Pool should have 1 connection");
|
||||
|
||||
// 设置连接为过期
|
||||
// 设置连接为过<EFBFBD><EFBFBD>?
|
||||
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
|
||||
HttpdnsNWReusableConnection *conn = connections.firstObject;
|
||||
[conn debugSetLastUsedDate:[NSDate dateWithTimeIntervalSinceNow:-31.0]];
|
||||
@@ -711,7 +711,7 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 直接验证池状态:过期连接已被移除,新连接已加入
|
||||
// 直接验证池状态:过期连接已被移除,新连接已加<EFBFBD><EFBFBD>?
|
||||
NSUInteger poolSizeAfter = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertEqual(poolSizeAfter, 1,
|
||||
@"Pool should have 1 connection (expired removed, new added)");
|
||||
@@ -721,7 +721,7 @@
|
||||
@"Should have created at least 1 new connection");
|
||||
}
|
||||
|
||||
// S.5 快速过期测试(无需等待)- 演示最佳实践
|
||||
// S.5 快速过期测试(无需等待<EFBFBD><EFBFBD>? 演示最佳实<EFBFBD><EFBFBD>?
|
||||
- (void)testIdleTimeout_FastExpiry_NoWaiting {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
@@ -740,7 +740,7 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 使用 DEBUG 辅助函数模拟 31 秒过期(无需实际等待)
|
||||
// 使用 DEBUG 辅助函数模拟 31 秒过期(无需实际等待<EFBFBD><EFBFBD>?
|
||||
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
|
||||
XCTAssertEqual(connections.count, 1);
|
||||
|
||||
@@ -766,7 +766,7 @@
|
||||
|
||||
CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - startTime;
|
||||
|
||||
// 关键验证:测试应该很快完成(< 5 秒),而非等待 30+ 秒
|
||||
// 关键验证:测试应该很快完成(< 5 秒),而非等待 30+ <EFBFBD><EFBFBD>?
|
||||
XCTAssertLessThan(elapsed, 5.0,
|
||||
@"Fast expiry test should complete quickly (%.1fs) without 30s wait", elapsed);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//
|
||||
// HttpdnsNWHTTPClient_StateMachineTests.m
|
||||
// AlicloudHttpDNSTests
|
||||
// TrustHttpDNSTests
|
||||
//
|
||||
// @author Created by Claude Code on 2025-11-01
|
||||
// Copyright © 2025 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2025 trustapp.com. All rights reserved.
|
||||
//
|
||||
// 状态机测试 - 包含状态机与异常场景 (Q) 测试组
|
||||
// 测试总数:17 个(Q:17)
|
||||
// 状态机测试 - 包含状态机与异常场<EFBFBD><EFBFBD>?(Q) 测试<EFBFBD><EFBFBD>?
|
||||
// 测试总数<EFBFBD><EFBFBD>?7 个(Q:17<EFBFBD><EFBFBD>?
|
||||
//
|
||||
|
||||
#import "HttpdnsNWHTTPClientTestBase.h"
|
||||
@@ -17,18 +17,18 @@
|
||||
|
||||
@implementation HttpdnsNWHTTPClient_StateMachineTests
|
||||
|
||||
#pragma mark - Q. 状态机与异常场景测试
|
||||
#pragma mark - Q. 状态机与异常场景测<EFBFBD><EFBFBD>?
|
||||
|
||||
// Q1.1 池溢出时LRU移除策略验证
|
||||
- (void)testStateMachine_PoolOverflowLRU_RemovesOldestByLastUsedDate {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 需要并发创建5个连接(串行请求会复用)
|
||||
// 需要并发创<EFBFBD><EFBFBD>?个连接(串行请求会复用)
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
// 并发发起5个请求
|
||||
// 并发发起5个请<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 5; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Request %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
@@ -42,15 +42,15 @@
|
||||
error:&error];
|
||||
[expectation fulfill];
|
||||
});
|
||||
[NSThread sleepForTimeInterval:0.05]; // 小间隔避免完全同时启动
|
||||
[NSThread sleepForTimeInterval:0.05]; // 小间隔避免完全同时启<EFBFBD><EFBFBD>?
|
||||
}
|
||||
|
||||
[self waitForExpectations:expectations timeout:20.0];
|
||||
|
||||
// 等待所有连接归还
|
||||
// 等待所有连接归<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证:池大小 ≤ 4(LRU移除溢出部分)
|
||||
// 验证:池大小 <EFBFBD><EFBFBD>?4(LRU移除溢出部分<EFBFBD><EFBFBD>?
|
||||
NSUInteger poolCount = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertLessThanOrEqual(poolCount, 4,
|
||||
@"Pool should enforce max 4 connections (LRU)");
|
||||
@@ -65,7 +65,7 @@
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 快速连续发起10个请求(测试连接归还的幂等性)
|
||||
// 快速连续发<EFBFBD><EFBFBD>?0个请求(测试连接归还的幂等性)
|
||||
for (NSInteger i = 0; i < 10; i++) {
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
@@ -78,12 +78,12 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证:池中最多1个连接(因为串行请求复用同一连接)
|
||||
// 验证:池中最<EFBFBD><EFBFBD>?个连接(因为串行请求复用同一连接<EFBFBD><EFBFBD>?
|
||||
NSUInteger poolCount = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertLessThanOrEqual(poolCount, 1,
|
||||
@"Pool should have at most 1 connection (rapid sequential reuse)");
|
||||
|
||||
// 验证:创建次数应该是1(所有请求复用同一连接)
|
||||
// 验证:创建次数应该是1(所有请求复用同一连接<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 1,
|
||||
@"Should create only 1 connection for sequential requests");
|
||||
}
|
||||
@@ -94,7 +94,7 @@
|
||||
NSString *poolKey11080 = @"127.0.0.1:11080:tcp";
|
||||
NSString *poolKey11443 = @"127.0.0.1:11443:tls";
|
||||
|
||||
// 向端口11080发起请求
|
||||
// 向端<EFBFBD><EFBFBD>?1080发起请求
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"Port11080"
|
||||
@@ -102,7 +102,7 @@
|
||||
error:&error1];
|
||||
XCTAssertNotNil(response1);
|
||||
|
||||
// 向端口11443发起请求
|
||||
// 向端<EFBFBD><EFBFBD>?1443发起请求
|
||||
NSError *error2 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"https://127.0.0.1:11443/get"
|
||||
userAgent:@"Port11443"
|
||||
@@ -113,7 +113,7 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
// 验证:两个池各自有1个连接
|
||||
// 验证:两个池各自<EFBFBD><EFBFBD>?个连<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey11080], 1,
|
||||
@"Port 11080 pool should have 1 connection");
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey11443], 1,
|
||||
@@ -128,7 +128,7 @@
|
||||
- (void)testInvariant_PoolSize_NeverExceedsLimit {
|
||||
[self.client resetPoolStatistics];
|
||||
|
||||
// 快速连续发起20个请求到同一端点
|
||||
// 快速连续发<EFBFBD><EFBFBD>?0个请求到同一端点
|
||||
for (NSInteger i = 0; i < 20; i++) {
|
||||
NSError *error = nil;
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
@@ -137,7 +137,7 @@
|
||||
error:&error];
|
||||
}
|
||||
|
||||
// 等待所有连接归还
|
||||
// 等待所有连接归<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:1.5];
|
||||
|
||||
// 验证:每个池的大小不超过4
|
||||
@@ -149,7 +149,7 @@
|
||||
key, (unsigned long)poolCount);
|
||||
}
|
||||
|
||||
// 验证:总连接数也不超过4(因为只有一个池)
|
||||
// 验证:总连接数也不超过4(因为只有一个池<EFBFBD><EFBFBD>?
|
||||
XCTAssertLessThanOrEqual([self.client totalConnectionCount], 4,
|
||||
@"Total connections should not exceed 4");
|
||||
}
|
||||
@@ -162,7 +162,7 @@
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
// 并发发起15个请求(可能复用连接)
|
||||
// 并发发起15个请求(可能复用连接<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 15; i++) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:[NSString stringWithFormat:@"Request %ld", (long)i]];
|
||||
[expectations addObject:expectation];
|
||||
@@ -182,17 +182,17 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证:池大小 ≤ 4(不变式)
|
||||
// 验证:池大小 <EFBFBD><EFBFBD>?4(不变式<EFBFBD><EFBFBD>?
|
||||
NSUInteger poolCount = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertLessThanOrEqual(poolCount, 4,
|
||||
@"Pool should not have duplicates (max 4 connections)");
|
||||
|
||||
// 验证:创建的连接数合理(≤15,因为可能有复用)
|
||||
// 验证:创建的连接数合理(<EFBFBD><EFBFBD>?5,因为可能有复用<EFBFBD><EFBFBD>?
|
||||
XCTAssertLessThanOrEqual(self.client.connectionCreationCount, 15,
|
||||
@"Should not create excessive connections");
|
||||
}
|
||||
|
||||
// Q4.1 边界条件:恰好30秒后连接过期
|
||||
// Q4.1 边界条件:恰<EFBFBD><EFBFBD>?0秒后连接过期
|
||||
- (void)testBoundary_Exactly30Seconds_ConnectionExpired {
|
||||
if (getenv("SKIP_SLOW_TESTS")) {
|
||||
return;
|
||||
@@ -200,7 +200,7 @@
|
||||
|
||||
[self.client resetPoolStatistics];
|
||||
|
||||
// 第一个请求
|
||||
// 第一个请<EFBFBD><EFBFBD>?
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"InitialRequest"
|
||||
@@ -211,7 +211,7 @@
|
||||
// 等待恰好30.5秒(超过30秒过期时间)
|
||||
[NSThread sleepForTimeInterval:30.5];
|
||||
|
||||
// 第二个请求:应该创建新连接(旧连接已过期)
|
||||
// 第二个请求:应该创建新连接(旧连接已过期<EFBFBD><EFBFBD>?
|
||||
NSError *error2 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"AfterExpiry"
|
||||
@@ -219,18 +219,18 @@
|
||||
error:&error2];
|
||||
XCTAssertNotNil(response2);
|
||||
|
||||
// 验证:创建了2个连接(旧连接过期,无法复用)
|
||||
// 验证:创建了2个连接(旧连接过期,无法复用<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 2,
|
||||
@"Should create 2 connections (first expired after 30s)");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 0,
|
||||
@"Should not reuse expired connection");
|
||||
}
|
||||
|
||||
// Q4.2 边界条件:29秒内连接未过期
|
||||
// Q4.2 边界条件<EFBFBD><EFBFBD>?9秒内连接未过<EFBFBD><EFBFBD>?
|
||||
- (void)testBoundary_Under30Seconds_ConnectionNotExpired {
|
||||
[self.client resetPoolStatistics];
|
||||
|
||||
// 第一个请求
|
||||
// 第一个请<EFBFBD><EFBFBD>?
|
||||
NSError *error1 = nil;
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"InitialRequest"
|
||||
@@ -249,19 +249,19 @@
|
||||
error:&error2];
|
||||
XCTAssertNotNil(response2);
|
||||
|
||||
// 验证:只创建了1个连接(复用了)
|
||||
// 验证:只创建<EFBFBD><EFBFBD>?个连接(复用了)
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 1,
|
||||
@"Should create only 1 connection (reused within 30s)");
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 1,
|
||||
@"Should reuse connection within 30s");
|
||||
}
|
||||
|
||||
// Q4.3 边界条件:恰好4个连接全部保留
|
||||
// Q4.3 边界条件:恰<EFBFBD><EFBFBD>?个连接全部保<EFBFBD><EFBFBD>?
|
||||
- (void)testBoundary_ExactlyFourConnections_AllKept {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 并发发起4个请求(使用延迟确保同时在飞行中,创建4个独立连接)
|
||||
// 并发发起4个请求(使用延迟确保同时在飞行中,创<EFBFBD><EFBFBD>?个独立连接)
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
@@ -272,7 +272,7 @@
|
||||
|
||||
dispatch_async(queue, ^{
|
||||
NSError *error = nil;
|
||||
// 使用 /delay/2 确保所有请求同时在飞行中
|
||||
// 使用 /delay/2 确保所有请求同时在飞行<EFBFBD><EFBFBD>?
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/2"
|
||||
userAgent:[NSString stringWithFormat:@"Request%ld", (long)i]
|
||||
timeout:15.0
|
||||
@@ -280,7 +280,7 @@
|
||||
[expectation fulfill];
|
||||
});
|
||||
|
||||
[NSThread sleepForTimeInterval:0.05]; // 小间隔避免完全同时启动
|
||||
[NSThread sleepForTimeInterval:0.05]; // 小间隔避免完全同时启<EFBFBD><EFBFBD>?
|
||||
}
|
||||
|
||||
[self waitForExpectations:expectations timeout:20.0];
|
||||
@@ -288,22 +288,22 @@
|
||||
// 等待连接归还
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证:池恰好有4个连接(全部保留)
|
||||
// 验证:池恰好<EFBFBD><EFBFBD>?个连接(全部保留<EFBFBD><EFBFBD>?
|
||||
NSUInteger poolCount = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertEqual(poolCount, 4,
|
||||
@"Pool should keep all 4 connections (not exceeding limit)");
|
||||
|
||||
// 验证:恰好创建4个连接
|
||||
// 验证:恰好创<EFBFBD><EFBFBD>?个连<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionCreationCount, 4,
|
||||
@"Should create exactly 4 connections");
|
||||
}
|
||||
|
||||
// Q1.2 正常状态序列验证
|
||||
// Q1.2 正常状态序列验<EFBFBD><EFBFBD>?
|
||||
- (void)testStateMachine_NormalSequence_StateTransitionsCorrect {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 第1步:创建并使用连接 (CREATING → IN_USE → IDLE)
|
||||
// <EFBFBD><EFBFBD>?步:创建并使用连<EFBFBD><EFBFBD>?(CREATING <EFBFBD><EFBFBD>?IN_USE <EFBFBD><EFBFBD>?IDLE)
|
||||
HttpdnsNWHTTPClientResponse *response1 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"StateTest"
|
||||
timeout:15.0
|
||||
@@ -312,18 +312,18 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:1.0]; // 等待归还
|
||||
|
||||
// 验证:池中有1个连接
|
||||
// 验证:池中有1个连<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client connectionPoolCountForKey:poolKey], 1,
|
||||
@"Connection should be in pool");
|
||||
|
||||
// 第2步:复用连接 (IDLE → IN_USE → IDLE)
|
||||
// <EFBFBD><EFBFBD>?步:复用连接 (IDLE <EFBFBD><EFBFBD>?IN_USE <EFBFBD><EFBFBD>?IDLE)
|
||||
HttpdnsNWHTTPClientResponse *response2 = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"StateTest"
|
||||
timeout:15.0
|
||||
error:nil];
|
||||
XCTAssertNotNil(response2, @"Second request should reuse connection");
|
||||
|
||||
// 验证:复用计数增加
|
||||
// 验证:复用计数增<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual(self.client.connectionReuseCount, 1,
|
||||
@"Should have reused connection once");
|
||||
}
|
||||
@@ -333,7 +333,7 @@
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 发起请求并归还
|
||||
// 发起请求并归<EFBFBD><EFBFBD>?
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"InUseTest"
|
||||
timeout:15.0
|
||||
@@ -364,20 +364,20 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 获取连接并设置 lastUsedDate 为 nil
|
||||
// 获取连接并设<EFBFBD><EFBFBD>?lastUsedDate <EFBFBD><EFBFBD>?nil
|
||||
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
|
||||
XCTAssertEqual(connections.count, 1, @"Should have connection");
|
||||
|
||||
HttpdnsNWReusableConnection *conn = connections.firstObject;
|
||||
[conn debugSetLastUsedDate:nil];
|
||||
|
||||
// 发起新请求触发 prune(内部应使用 distantPast 处理 nil)
|
||||
// 发起新请求触<EFBFBD><EFBFBD>?prune(内部应使用 distantPast 处理 nil<EFBFBD><EFBFBD>?
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"NilDateTest"
|
||||
timeout:15.0
|
||||
error:nil];
|
||||
|
||||
// 验证:不崩溃,正常工作
|
||||
// 验证:不崩溃,正常工<EFBFBD><EFBFBD>?
|
||||
XCTAssertNotNil(response, @"Should handle nil lastUsedDate gracefully");
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 发起多个请求(包括成功和超时)
|
||||
// 发起多个请求(包括成功和超时<EFBFBD><EFBFBD>?
|
||||
for (NSInteger i = 0; i < 3; i++) {
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"InvariantTest"
|
||||
@@ -394,7 +394,7 @@
|
||||
error:nil];
|
||||
}
|
||||
|
||||
// 发起1个超时请求
|
||||
// 发起1个超时请<EFBFBD><EFBFBD>?
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
|
||||
userAgent:@"TimeoutTest"
|
||||
timeout:0.5
|
||||
@@ -402,7 +402,7 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:2.0];
|
||||
|
||||
// 获取池中所有连接
|
||||
// 获取池中所有连<EFBFBD><EFBFBD>?
|
||||
NSArray<HttpdnsNWReusableConnection *> *connections = [self.client connectionsInPoolForKey:poolKey];
|
||||
|
||||
// 验证:池中无失效连接
|
||||
@@ -411,12 +411,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Q3.4 lastUsedDate 单调性验证
|
||||
// Q3.4 lastUsedDate 单调性验<EFBFBD><EFBFBD>?
|
||||
- (void)testInvariant_LastUsedDate_Monotonic {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 第1次使用
|
||||
// <EFBFBD><EFBFBD>?次使<EFBFBD><EFBFBD>?
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"MonotonicTest"
|
||||
timeout:15.0
|
||||
@@ -429,10 +429,10 @@
|
||||
NSDate *date1 = connections1.firstObject.lastUsedDate;
|
||||
XCTAssertNotNil(date1, @"lastUsedDate should be set");
|
||||
|
||||
// 等待1秒确保时间推进
|
||||
// 等待1秒确保时间推<EFBFBD><EFBFBD>?
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 第2次使用同一连接
|
||||
// <EFBFBD><EFBFBD>?次使用同一连接
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"MonotonicTest"
|
||||
timeout:15.0
|
||||
@@ -449,12 +449,12 @@
|
||||
@"lastUsedDate should increase after reuse");
|
||||
}
|
||||
|
||||
// Q5.1 超时+池溢出复合场景
|
||||
// Q5.1 超时+池溢出复合场<EFBFBD><EFBFBD>?
|
||||
- (void)testCompound_TimeoutDuringPoolOverflow_Handled {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
|
||||
// 先填满池(4个成功连接)
|
||||
// 先填满池<EFBFBD><EFBFBD>?个成功连接)
|
||||
NSMutableArray<XCTestExpectation *> *expectations = [NSMutableArray array];
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
@@ -477,9 +477,9 @@
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
NSUInteger poolCountBefore = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertLessThanOrEqual(poolCountBefore, 4, @"Pool should have ≤4 connections");
|
||||
XCTAssertLessThanOrEqual(poolCountBefore, 4, @"Pool should have <EFBFBD><EFBFBD>? connections");
|
||||
|
||||
// 第5个请求超时
|
||||
// <EFBFBD><EFBFBD>?个请求超<EFBFBD><EFBFBD>?
|
||||
NSError *error = nil;
|
||||
HttpdnsNWHTTPClientResponse *response = [self.client performRequestWithURLString:@"http://127.0.0.1:11080/delay/10"
|
||||
userAgent:@"TimeoutRequest"
|
||||
@@ -491,7 +491,7 @@
|
||||
|
||||
[NSThread sleepForTimeInterval:1.0];
|
||||
|
||||
// 验证:超时连接未加入池
|
||||
// 验证:超时连接未加入<EFBFBD><EFBFBD>?
|
||||
NSUInteger poolCountAfter = [self.client connectionPoolCountForKey:poolKey];
|
||||
XCTAssertLessThanOrEqual(poolCountAfter, 4, @"Timed-out connection should not be added to pool");
|
||||
}
|
||||
@@ -507,15 +507,15 @@
|
||||
timeout:2.0
|
||||
error:&error];
|
||||
|
||||
// 验证:请求失败
|
||||
// 验证:请求失<EFBFBD><EFBFBD>?
|
||||
XCTAssertNil(response, @"Should fail to connect to invalid port");
|
||||
|
||||
// 验证:无连接加入池
|
||||
// 验证:无连接加入<EFBFBD><EFBFBD>?
|
||||
XCTAssertEqual([self.client totalConnectionCount], 0,
|
||||
@"Failed connection should not be added to pool");
|
||||
}
|
||||
|
||||
// Q2.5 多次 invalidate 幂等性
|
||||
// Q2.5 多次 invalidate 幂等<EFBFBD><EFBFBD>?
|
||||
- (void)testAbnormal_MultipleInvalidate_Idempotent {
|
||||
[self.client resetPoolStatistics];
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
@@ -542,11 +542,11 @@
|
||||
XCTAssertTrue(conn.isInvalidated, @"Connection should be invalidated");
|
||||
}
|
||||
|
||||
// Q5.2 并发 dequeue 竞态测试
|
||||
// Q5.2 并发 dequeue 竞态测<EFBFBD><EFBFBD>?
|
||||
- (void)testCompound_ConcurrentDequeueDuringPrune_Safe {
|
||||
[self.client resetPoolStatistics];
|
||||
|
||||
// 在两个端口创建连接
|
||||
// 在两个端口创建连<EFBFBD><EFBFBD>?
|
||||
[self.client performRequestWithURLString:@"http://127.0.0.1:11080/get"
|
||||
userAgent:@"RaceTest"
|
||||
timeout:15.0
|
||||
@@ -562,7 +562,7 @@
|
||||
// 等待30秒让连接过期
|
||||
[NSThread sleepForTimeInterval:30.5];
|
||||
|
||||
// 并发触发两个端口的 dequeue(会触发 prune)
|
||||
// 并发触发两个端口<EFBFBD><EFBFBD>?dequeue(会触发 prune<EFBFBD><EFBFBD>?
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
|
||||
|
||||
@@ -1,78 +1,78 @@
|
||||
# HTTP Mock Server for Integration Tests
|
||||
|
||||
本目录包含用于 `HttpdnsNWHTTPClient` 集成测试的 HTTP/HTTPS mock server,用于替代 httpbin.org。
|
||||
本目录包含用<EFBFBD><EFBFBD>?`HttpdnsNWHTTPClient` 集成测试<EFBFBD><EFBFBD>?HTTP/HTTPS mock server,用于替<EFBFBD><EFBFBD>?httpbin.org<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
## 为什么需要 Mock Server?
|
||||
## 为什么需<EFBFBD><EFBFBD>?Mock Server<EFBFBD><EFBFBD>?
|
||||
|
||||
1. **可靠性**: httpbin.org 在高并发测试下表现不稳定,经常返回非预期的 HTTP 状态码(如 429 Too Many Requests)
|
||||
1. **可靠<EFBFBD><EFBFBD>?*: httpbin.org 在高并发测试下表现不稳定,经常返回非预期<EFBFBD><EFBFBD>?HTTP 状态码(如 429 Too Many Requests<EFBFBD><EFBFBD>?
|
||||
2. **速度**: 本地服务器响应更快,缩短测试执行时间
|
||||
3. **离线测试**: 无需网络连接即可运行集成测试
|
||||
4. **可控性**: 完全掌控测试环境,便于调试和复现问题
|
||||
4. **可控<EFBFBD><EFBFBD>?*: 完全掌控测试环境,便于调试和复现问题
|
||||
|
||||
---
|
||||
|
||||
## 快速开始
|
||||
## 快速开<EFBFBD><EFBFBD>?
|
||||
|
||||
### 1. 启动 Mock Server
|
||||
|
||||
```bash
|
||||
# 进入测试目录
|
||||
cd AlicloudHttpDNSTests/Network
|
||||
cd TrustHttpDNSTests/Network
|
||||
|
||||
# 启动服务器(无需 sudo 权限,使用非特权端口)
|
||||
# 启动服务器(无需 sudo 权限,使用非特权端口<EFBFBD><EFBFBD>?
|
||||
python3 mock_server.py
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- **无需 root 权限**(使用非特权端口 11080/11443-11446)
|
||||
- **无需 root 权限**(使用非特权端口 11080/11443-11446<EFBFBD><EFBFBD>?
|
||||
- 首次运行会自动生成自签名证书 (`server.pem`)
|
||||
- 按 `Ctrl+C` 停止服务器
|
||||
- <EFBFBD><EFBFBD>?`Ctrl+C` 停止服务<EFBFBD><EFBFBD>?
|
||||
|
||||
### 2. 运行集成测试
|
||||
|
||||
在另一个终端窗口:
|
||||
在另一个终端窗<EFBFBD><EFBFBD>?
|
||||
|
||||
```bash
|
||||
cd ~/Project/iOS/alicloud-ios-sdk-httpdns
|
||||
cd ~/Project/iOS/Trust-ios-sdk-httpdns
|
||||
|
||||
# 运行所有集成测试
|
||||
# 运行所有集成测<EFBFBD><EFBFBD>?
|
||||
xcodebuild test \
|
||||
-workspace AlicloudHttpDNS.xcworkspace \
|
||||
-scheme AlicloudHttpDNSTests \
|
||||
-workspace TrustHttpDNS.xcworkspace \
|
||||
-scheme TrustHttpDNSTests \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
-only-testing:AlicloudHttpDNSTests/HttpdnsNWHTTPClientIntegrationTests
|
||||
-only-testing:TrustHttpDNSTests/HttpdnsNWHTTPClientIntegrationTests
|
||||
|
||||
# 运行单个测试
|
||||
xcodebuild test \
|
||||
-workspace AlicloudHttpDNS.xcworkspace \
|
||||
-scheme AlicloudHttpDNSTests \
|
||||
-workspace TrustHttpDNS.xcworkspace \
|
||||
-scheme TrustHttpDNSTests \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
-only-testing:AlicloudHttpDNSTests/HttpdnsNWHTTPClientIntegrationTests/testConcurrency_ParallelRequestsSameHost_AllSucceed
|
||||
-only-testing:TrustHttpDNSTests/HttpdnsNWHTTPClientIntegrationTests/testConcurrency_ParallelRequestsSameHost_AllSucceed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 支持的 Endpoints
|
||||
## 支持<EFBFBD><EFBFBD>?Endpoints
|
||||
|
||||
Mock server 实现了以下 httpbin.org 兼容的 endpoints:
|
||||
Mock server 实现了以<EFBFBD><EFBFBD>?httpbin.org 兼容<EFBFBD><EFBFBD>?endpoints:
|
||||
|
||||
| Endpoint | 功能 | 示例 |
|
||||
|----------|------|------|
|
||||
| `GET /get` | 返回请求信息(headers, args, origin) | `http://127.0.0.1:11080/get` |
|
||||
| `GET /status/{code}` | 返回指定状态码(100-599) | `http://127.0.0.1:11080/status/404` |
|
||||
| `GET /stream-bytes/{n}` | 返回 chunked 编码的 N 字节数据 | `http://127.0.0.1:11080/stream-bytes/1024` |
|
||||
| `GET /delay/{seconds}` | 延迟指定秒数后返回(最多10秒) | `http://127.0.0.1:11080/delay/5` |
|
||||
| `GET /headers` | 返回所有请求头部 | `http://127.0.0.1:11080/headers` |
|
||||
| `GET /get` | 返回请求信息(headers, args, origin<EFBFBD><EFBFBD>?| `http://127.0.0.1:11080/get` |
|
||||
| `GET /status/{code}` | 返回指定状态码<EFBFBD><EFBFBD>?00-599<EFBFBD><EFBFBD>?| `http://127.0.0.1:11080/status/404` |
|
||||
| `GET /stream-bytes/{n}` | 返回 chunked 编码<EFBFBD><EFBFBD>?N 字节数据 | `http://127.0.0.1:11080/stream-bytes/1024` |
|
||||
| `GET /delay/{seconds}` | 延迟指定秒数后返回(最<EFBFBD><EFBFBD>?0秒) | `http://127.0.0.1:11080/delay/5` |
|
||||
| `GET /headers` | 返回所有请求头<EFBFBD><EFBFBD>?| `http://127.0.0.1:11080/headers` |
|
||||
| `GET /uuid` | 返回随机 UUID | `http://127.0.0.1:11080/uuid` |
|
||||
| `GET /user-agent` | 返回 User-Agent 头部 | `http://127.0.0.1:11080/user-agent` |
|
||||
|
||||
**端口配置**:
|
||||
- **HTTP**: `127.0.0.1:11080`
|
||||
- **HTTPS**: `127.0.0.1:11443`, `11444`, `11445`, `11446`(4个端口用于测试连接池隔离)
|
||||
- **HTTPS**: `127.0.0.1:11443`, `11444`, `11445`, `11446`<EFBFBD><EFBFBD>?个端口用于测试连接池隔离<EFBFBD><EFBFBD>?
|
||||
|
||||
所有 endpoints 在 HTTP 和 HTTPS 端口上均可访问。
|
||||
所<EFBFBD><EFBFBD>?endpoints <EFBFBD><EFBFBD>?HTTP <EFBFBD><EFBFBD>?HTTPS 端口上均可访问<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
@@ -80,25 +80,25 @@ Mock server 实现了以下 httpbin.org 兼容的 endpoints:
|
||||
|
||||
### 架构
|
||||
|
||||
- **HTTP 服务器**: 监听 `127.0.0.1:11080`
|
||||
- **HTTPS 服务器**: 监听 `127.0.0.1:11443`, `11444`, `11445`, `11446`(4个端口,使用自签名证书)
|
||||
- **多端口目的**: 测试连接池的端口隔离机制,确保不同端口使用独立的连接池
|
||||
- **并发模型**: 多线程(`ThreadingMixIn`),支持高并发请求
|
||||
- **HTTP 服务<EFBFBD><EFBFBD>?*: 监听 `127.0.0.1:11080`
|
||||
- **HTTPS 服务<EFBFBD><EFBFBD>?*: 监听 `127.0.0.1:11443`, `11444`, `11445`, `11446`<EFBFBD><EFBFBD>?个端口,使用自签名证书)
|
||||
- **多端口目<EFBFBD><EFBFBD>?*: 测试连接池的端口隔离机制,确保不同端口使用独立的连接<EFBFBD><EFBFBD>?
|
||||
- **并发模型**: 多线程(`ThreadingMixIn`),支持高并发请<EFBFBD><EFBFBD>?
|
||||
|
||||
### TLS 证书
|
||||
|
||||
- 自动生成自签名证书(RSA 2048位,有效期 365 天)
|
||||
- 自动生成自签名证书(RSA 2048位,有效<EFBFBD><EFBFBD>?365 天)
|
||||
- CN (Common Name): `localhost`
|
||||
- 证书文件: `server.pem`(同时包含密钥和证书)
|
||||
- 证书文件: `server.pem`(同时包含密钥和证书<EFBFBD><EFBFBD>?
|
||||
|
||||
**重要**: 集成测试通过环境变量 `HTTPDNS_SKIP_TLS_VERIFY=1` 跳过 TLS 验证,这是安全的,因为:
|
||||
1. 仅在测试环境生效
|
||||
2. 不影响生产代码
|
||||
3. 连接限制为本地 loopback (127.0.0.1)
|
||||
2. 不影响生产代<EFBFBD><EFBFBD>?
|
||||
3. 连接限制为本<EFBFBD><EFBFBD>?loopback (127.0.0.1)
|
||||
|
||||
### 响应格式
|
||||
|
||||
所有 JSON 响应遵循 httpbin.org 格式,例如:
|
||||
所<EFBFBD><EFBFBD>?JSON 响应遵循 httpbin.org 格式,例<EFBFBD><EFBFBD>?
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -131,7 +131,7 @@ XXXXXXXXXX
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
✗ 端口 80 已被占用,请关闭占用端口的进程或使用其他端口
|
||||
<EFBFBD><EFBFBD>?端口 80 已被占用,请关闭占用端口的进程或使用其他端口
|
||||
```
|
||||
|
||||
**解决方法**:
|
||||
@@ -147,9 +147,9 @@ sudo lsof -i :443
|
||||
sudo kill -9 <PID>
|
||||
```
|
||||
|
||||
3. 或修改 mock_server.py 使用其他端口:
|
||||
3. 或修<EFBFBD><EFBFBD>?mock_server.py 使用其他端口:
|
||||
```python
|
||||
# 修改端口号(同时需要更新测试代码中的 URL)
|
||||
# 修改端口号(同时需要更新测试代码中<EFBFBD><EFBFBD>?URL<52><4C>?
|
||||
run_http_server(port=8080)
|
||||
run_https_server(port=8443)
|
||||
```
|
||||
@@ -158,13 +158,13 @@ run_https_server(port=8443)
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
✗ 未找到 openssl 命令,请安装 OpenSSL
|
||||
<EFBFBD><EFBFBD>?未找<E69CAA><E689BE>?openssl 命令,请安装 OpenSSL
|
||||
```
|
||||
|
||||
**解决方法**:
|
||||
|
||||
```bash
|
||||
# macOS (通常已预装)
|
||||
# macOS (通常已预<EFBFBD><EFBFBD>?
|
||||
brew install openssl
|
||||
|
||||
# Ubuntu/Debian
|
||||
@@ -174,11 +174,11 @@ sudo apt-get install openssl
|
||||
sudo yum install openssl
|
||||
```
|
||||
|
||||
### 权限被拒绝
|
||||
### 权限被拒<EFBFBD><EFBFBD>?
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
✗ 错误: 需要 root 权限以绑定 80/443 端口
|
||||
<EFBFBD><EFBFBD>?错误: 需<EFBFBD><EFBFBD>?root 权限以绑<EFBFBD><EFBFBD>?80/443 端口
|
||||
```
|
||||
|
||||
**解决方法**:
|
||||
@@ -190,21 +190,21 @@ sudo python3 mock_server.py
|
||||
|
||||
---
|
||||
|
||||
## 切换回 httpbin.org
|
||||
## 切换<EFBFBD><EFBFBD>?httpbin.org
|
||||
|
||||
如需使用真实的 httpbin.org 进行测试(例如验证兼容性):
|
||||
如需使用真实<EFBFBD><EFBFBD>?httpbin.org 进行测试(例如验证兼容性):
|
||||
|
||||
1. 编辑 `HttpdnsNWHTTPClientIntegrationTests.m`
|
||||
2. 将所有 `127.0.0.1` 替换回 `httpbin.org`
|
||||
3. 注释掉 setUp/tearDown 中的环境变量设置
|
||||
2. 将所<EFBFBD><EFBFBD>?`127.0.0.1` 替换<EFBFBD><EFBFBD>?`httpbin.org`
|
||||
3. 注释<EFBFBD><EFBFBD>?setUp/tearDown 中的环境变量设置
|
||||
|
||||
---
|
||||
|
||||
## 开发与扩展
|
||||
|
||||
### 添加新 Endpoint
|
||||
### 添加<EFBFBD><EFBFBD>?Endpoint
|
||||
|
||||
在 `mock_server.py` 的 `MockHTTPHandler.do_GET()` 方法中添加:
|
||||
<EFBFBD><EFBFBD>?`mock_server.py` <EFBFBD><EFBFBD>?`MockHTTPHandler.do_GET()` 方法中添<EFBFBD><EFBFBD>?
|
||||
|
||||
```python
|
||||
def do_GET(self):
|
||||
@@ -215,14 +215,14 @@ def do_GET(self):
|
||||
# ... 其他 endpoints
|
||||
|
||||
def _handle_your_endpoint(self):
|
||||
"""处理自定义 endpoint"""
|
||||
"""处理自定<EFBFBD><EFBFBD>?endpoint"""
|
||||
data = {'custom': 'data'}
|
||||
self._send_json(200, data)
|
||||
```
|
||||
|
||||
### 调试模式
|
||||
|
||||
取消注释 `log_message` 方法以启用详细日志:
|
||||
取消注释 `log_message` 方法以启用详细日<EFBFBD><EFBFBD>?
|
||||
|
||||
```python
|
||||
def log_message(self, format, *args):
|
||||
@@ -234,20 +234,20 @@ def log_message(self, format, *args):
|
||||
## 技术栈
|
||||
|
||||
- **Python 3.7+** (标准库,无需额外依赖)
|
||||
- **http.server**: HTTP 服务器实现
|
||||
- **http.server**: HTTP 服务器实<EFBFBD><EFBFBD>?
|
||||
- **ssl**: TLS/SSL 支持
|
||||
- **socketserver.ThreadingMixIn**: 多线程并发
|
||||
- **socketserver.ThreadingMixIn**: 多线程并<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
## 安全注意事项
|
||||
|
||||
1. **仅用于测试**: 此服务器设计用于本地测试,不适合生产环境
|
||||
2. **自签名证书**: HTTPS 使用不受信任的自签名证书
|
||||
3. **无身份验证**: 不实现任何身份验证机制
|
||||
4. **本地绑定**: 服务器仅绑定到 `127.0.0.1`,不接受外部连接
|
||||
1. **仅用于测<EFBFBD><EFBFBD>?*: 此服务器设计用于本地测试,不适合生产环境
|
||||
2. **自签名证<EFBFBD><EFBFBD>?*: HTTPS 使用不受信任的自签名证书
|
||||
3. **无身份验<EFBFBD><EFBFBD>?*: 不实现任何身份验证机<EFBFBD><EFBFBD>?
|
||||
4. **本地绑定**: 服务器仅绑定<EFBFBD><EFBFBD>?`127.0.0.1`,不接受外部连接
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2025-11-01
|
||||
**维护者**: Claude Code
|
||||
**最后更<EFBFBD><EFBFBD>?*: 2025-11-01
|
||||
**维护<EFBFBD><EFBFBD>?*: Claude Code
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
# HttpdnsNWHTTPClient 测试套件
|
||||
|
||||
本目录包含 `HttpdnsNWHTTPClient` 和 `HttpdnsNWReusableConnection` 的完整测试套件。
|
||||
本目录包<EFBFBD><EFBFBD>?`HttpdnsNWHTTPClient` <EFBFBD><EFBFBD>?`HttpdnsNWReusableConnection` 的完整测试套件<EFBFBD><EFBFBD>?
|
||||
|
||||
## 测试文件结构
|
||||
|
||||
```
|
||||
AlicloudHttpDNSTests/Network/
|
||||
TrustHttpDNSTests/Network/
|
||||
├── HttpdnsNWHTTPClientTests.m # 主单元测试(44个)
|
||||
├── HttpdnsNWHTTPClientIntegrationTests.m # 集成测试(7个)
|
||||
├── HttpdnsNWHTTPClientTestHelper.h/m # 测试辅助工具类
|
||||
└── README.md # 本文件
|
||||
├── HttpdnsNWHTTPClientIntegrationTests.m # 集成测试<EFBFBD><EFBFBD>?个)
|
||||
├── HttpdnsNWHTTPClientTestHelper.h/m # 测试辅助工具<EFBFBD><EFBFBD>?
|
||||
└── README.md # 本文<EFBFBD><EFBFBD>?
|
||||
```
|
||||
|
||||
## 测试覆盖范围
|
||||
|
||||
### 单元测试 (HttpdnsNWHTTPClientTests.m)
|
||||
|
||||
#### A. HTTP 解析逻辑测试 (25个)
|
||||
- **A1. Header 解析 (9个)**
|
||||
#### A. HTTP 解析逻辑测试 (25<EFBFBD><EFBFBD>?
|
||||
- **A1. Header 解析 (9<EFBFBD><EFBFBD>?**
|
||||
- 正常响应解析
|
||||
- 多个头部字段
|
||||
- 不完整数据处理
|
||||
- 不完整数据处<EFBFBD><EFBFBD>?
|
||||
- 无效状态行
|
||||
- 空格处理与 trim
|
||||
- 空值头部
|
||||
- 空格处理<EFBFBD><EFBFBD>?trim
|
||||
- 空值头<EFBFBD><EFBFBD>?
|
||||
- 非数字状态码
|
||||
- 状态码为零
|
||||
- 无效头部行
|
||||
- 无效头部<EFBFBD><EFBFBD>?
|
||||
|
||||
- **A2. Chunked 编码检查 (8个)**
|
||||
- **A2. Chunked 编码检<EFBFBD><EFBFBD>?(8<><38>?**
|
||||
- 单个 chunk
|
||||
- 多个 chunks
|
||||
- 不完整 chunk
|
||||
- 不完<EFBFBD><EFBFBD>?chunk
|
||||
- Chunk extension 支持
|
||||
- 无效十六进制 size
|
||||
- Chunk size 溢出
|
||||
- 缺少 CRLF 终止符
|
||||
- 带 trailers 的 chunked
|
||||
- 缺少 CRLF 终止<EFBFBD><EFBFBD>?
|
||||
- <EFBFBD><EFBFBD>?trailers <EFBFBD><EFBFBD>?chunked
|
||||
|
||||
- **A3. Chunked 解码 (2个)**
|
||||
- **A3. Chunked 解码 (2<EFBFBD><EFBFBD>?**
|
||||
- 多个 chunks 正确解码
|
||||
- 无效格式返回 nil
|
||||
|
||||
- **A4. 完整响应解析 (6个)**
|
||||
- **A4. 完整响应解析 (6<EFBFBD><EFBFBD>?**
|
||||
- Content-Length 响应
|
||||
- Chunked 编码响应
|
||||
- 空 body
|
||||
- Content-Length 不匹配
|
||||
- 空数据错误
|
||||
- 只有 headers 无 body
|
||||
- <EFBFBD><EFBFBD>?body
|
||||
- Content-Length 不匹<EFBFBD><EFBFBD>?
|
||||
- 空数据错<EFBFBD><EFBFBD>?
|
||||
- 只有 headers <EFBFBD><EFBFBD>?body
|
||||
|
||||
#### C. 请求构建测试 (7个)
|
||||
#### C. 请求构建测试 (7<EFBFBD><EFBFBD>?
|
||||
- 基本 GET 请求格式
|
||||
- 查询参数处理
|
||||
- User-Agent 头部
|
||||
- HTTP 默认端口处理
|
||||
- HTTPS 默认端口处理
|
||||
- 非默认端口显示
|
||||
- 非默认端口显<EFBFBD><EFBFBD>?
|
||||
- 固定头部验证
|
||||
|
||||
#### E. TLS 验证测试 (4个占位符)
|
||||
@@ -65,95 +65,95 @@ AlicloudHttpDNSTests/Network/
|
||||
- 无效证书返回 NO
|
||||
- 指定域名使用 SSL Policy
|
||||
|
||||
*注:TLS 测试需要真实的 SecTrustRef 或复杂 mock,当前为占位符*
|
||||
*注:TLS 测试需要真实的 SecTrustRef 或复<EFBFBD><EFBFBD>?mock,当前为占位<EFBFBD><EFBFBD>?
|
||||
|
||||
#### F. 边缘情况测试 (8个)
|
||||
#### F. 边缘情况测试 (8<EFBFBD><EFBFBD>?
|
||||
- 超长 URL 处理
|
||||
- 空 User-Agent
|
||||
- 超大响应体(5MB)
|
||||
- <EFBFBD><EFBFBD>?User-Agent
|
||||
- 超大响应体(5MB<EFBFBD><EFBFBD>?
|
||||
- Chunked 解码失败回退
|
||||
- 连接池 key - 不同 hosts
|
||||
- 连接池 key - 不同 ports
|
||||
- 连接池 key - HTTP vs HTTPS
|
||||
- 连接<EFBFBD><EFBFBD>?key - 不同 hosts
|
||||
- 连接<EFBFBD><EFBFBD>?key - 不同 ports
|
||||
- 连接<EFBFBD><EFBFBD>?key - HTTP vs HTTPS
|
||||
|
||||
### 集成测试 (HttpdnsNWHTTPClientIntegrationTests.m)
|
||||
|
||||
使用 httpbin.org 进行真实网络测试 (22个):
|
||||
使用 httpbin.org 进行真实网络测试 (22<EFBFBD><EFBFBD>?<3F><>?
|
||||
|
||||
**G. 基础集成测试 (7个)**
|
||||
**G. 基础集成测试 (7<EFBFBD><EFBFBD>?**
|
||||
- HTTP GET 请求
|
||||
- HTTPS GET 请求
|
||||
- HTTP 404 响应
|
||||
- 连接复用(两次请求)
|
||||
- Chunked 响应处理
|
||||
- 请求超时测试
|
||||
- 自定义头部验证
|
||||
- 自定义头部验<EFBFBD><EFBFBD>?
|
||||
|
||||
**H. 并发测试 (5个)**
|
||||
- 并发请求同一主机(10个线程)
|
||||
- 并发请求不同路径(5个不同endpoint)
|
||||
**H. 并发测试 (5<EFBFBD><EFBFBD>?**
|
||||
- 并发请求同一主机<EFBFBD><EFBFBD>?0个线程)
|
||||
- 并发请求不同路径<EFBFBD><EFBFBD>?个不同endpoint<EFBFBD><EFBFBD>?
|
||||
- 混合 HTTP + HTTPS 并发(各5个线程)
|
||||
- 高负载压力测试(50个并发请求)
|
||||
- 混合串行+并发模式
|
||||
|
||||
**I. 竞态条件测试 (5个)**
|
||||
**I. 竞态条件测<EFBFBD><EFBFBD>?(5<><35>?**
|
||||
- 连接池容量测试(超过4个连接上限)
|
||||
- 同时归还连接(5个并发)
|
||||
- 获取-归还-再获取竞态
|
||||
- 同时归还连接<EFBFBD><EFBFBD>?个并发)
|
||||
- 获取-归还-再获取竞<EFBFBD><EFBFBD>?
|
||||
- 超时与活跃连接冲突(需31秒,可跳过)
|
||||
- 错误恢复后连接池健康状态
|
||||
- 错误恢复后连接池健康状<EFBFBD><EFBFBD>?
|
||||
|
||||
**J. 高级连接复用测试 (5个)**
|
||||
**J. 高级连接复用测试 (5<EFBFBD><EFBFBD>?**
|
||||
- 连接过期与清理(31秒,可跳过)
|
||||
- 连接池容量限制验证(10个连续请求)
|
||||
- 不同路径复用连接(4个不同路径)
|
||||
- 不同路径复用连接<EFBFBD><EFBFBD>?个不同路径)
|
||||
- HTTP vs HTTPS 使用不同连接池key
|
||||
- 长连接保持测试(20个请求间隔1秒,可跳过)
|
||||
- 长连接保持测试(20个请求间<EFBFBD><EFBFBD>?秒,可跳过)
|
||||
|
||||
## 运行测试
|
||||
|
||||
### 运行所有单元测试
|
||||
### 运行所有单元测<EFBFBD><EFBFBD>?
|
||||
```bash
|
||||
xcodebuild test \
|
||||
-workspace AlicloudHttpDNS.xcworkspace \
|
||||
-scheme AlicloudHttpDNSTests \
|
||||
-workspace TrustHttpDNS.xcworkspace \
|
||||
-scheme TrustHttpDNSTests \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
-only-testing:AlicloudHttpDNSTests/HttpdnsNWHTTPClientTests
|
||||
-only-testing:TrustHttpDNSTests/HttpdnsNWHTTPClientTests
|
||||
```
|
||||
|
||||
### 运行集成测试(需要网络)
|
||||
```bash
|
||||
xcodebuild test \
|
||||
-workspace AlicloudHttpDNS.xcworkspace \
|
||||
-scheme AlicloudHttpDNSTests \
|
||||
-workspace TrustHttpDNS.xcworkspace \
|
||||
-scheme TrustHttpDNSTests \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
-only-testing:AlicloudHttpDNSTests/HttpdnsNWHTTPClientIntegrationTests
|
||||
-only-testing:TrustHttpDNSTests/HttpdnsNWHTTPClientIntegrationTests
|
||||
```
|
||||
|
||||
### 运行单个测试
|
||||
```bash
|
||||
xcodebuild test \
|
||||
-workspace AlicloudHttpDNS.xcworkspace \
|
||||
-scheme AlicloudHttpDNSTests \
|
||||
-workspace TrustHttpDNS.xcworkspace \
|
||||
-scheme TrustHttpDNSTests \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
-only-testing:AlicloudHttpDNSTests/HttpdnsNWHTTPClientTests/testParseHTTPHeaders_ValidResponse_Success
|
||||
-only-testing:TrustHttpDNSTests/HttpdnsNWHTTPClientTests/testParseHTTPHeaders_ValidResponse_Success
|
||||
```
|
||||
|
||||
## 测试辅助工具
|
||||
|
||||
### HttpdnsNWHTTPClientTestHelper
|
||||
|
||||
提供以下工具方法:
|
||||
提供以下工具方法<EFBFBD><EFBFBD>?
|
||||
|
||||
#### HTTP 响应构造
|
||||
#### HTTP 响应构<EFBFBD><EFBFBD>?
|
||||
```objc
|
||||
// 构造标准 HTTP 响应
|
||||
// 构造标<EFBFBD><EFBFBD>?HTTP 响应
|
||||
+ (NSData *)createHTTPResponseWithStatus:(NSInteger)statusCode
|
||||
statusText:(NSString *)statusText
|
||||
headers:(NSDictionary *)headers
|
||||
body:(NSData *)body;
|
||||
|
||||
// 构造 chunked 响应
|
||||
// 构<EFBFBD><EFBFBD>?chunked 响应
|
||||
+ (NSData *)createChunkedHTTPResponseWithStatus:(NSInteger)statusCode
|
||||
headers:(NSDictionary *)headers
|
||||
chunks:(NSArray<NSData *> *)chunks;
|
||||
@@ -175,19 +175,19 @@ xcodebuild test \
|
||||
|
||||
| 测试类别 | 测试数量 | 覆盖范围 |
|
||||
|---------|---------|---------|
|
||||
| HTTP 解析 | 25 | HTTP 头部、Chunked 编码、完整响应 |
|
||||
| 请求构建 | 7 | URL 处理、头部生成 |
|
||||
| TLS 验证 | 4 (占位符) | 证书验证 |
|
||||
| HTTP 解析 | 25 | HTTP 头部、Chunked 编码、完整响<EFBFBD><EFBFBD>?|
|
||||
| 请求构建 | 7 | URL 处理、头部生<EFBFBD><EFBFBD>?|
|
||||
| TLS 验证 | 4 (占位<EFBFBD><EFBFBD>? | 证书验证 |
|
||||
| 边缘情况 | 8 | 异常输入、连接池 key |
|
||||
| **单元测试合计** | **43** | - |
|
||||
| 基础集成测试 (G) | 7 | 真实网络请求、基本场景 |
|
||||
| 基础集成测试 (G) | 7 | 真实网络请求、基本场<EFBFBD><EFBFBD>?|
|
||||
| 并发测试 (H) | 5 | 多线程并发、高负载 |
|
||||
| 竞态条件测试 (I) | 5 | 连接池竞态、错误恢复 |
|
||||
| 连接复用测试 (J) | 5 | 连接过期、长连接、协议隔离 |
|
||||
| 多端口连接隔离 (K) | 5 | 不同端口独立连接池 |
|
||||
| 竞态条件测<EFBFBD><EFBFBD>?(I) | 5 | 连接池竞态、错误恢<EFBFBD><EFBFBD>?|
|
||||
| 连接复用测试 (J) | 5 | 连接过期、长连接、协议隔<EFBFBD><EFBFBD>?|
|
||||
| 多端口连接隔<EFBFBD><EFBFBD>?(K) | 5 | 不同端口独立连接<EFBFBD><EFBFBD>?|
|
||||
| 端口池耗尽测试 (L) | 3 | 多端口高负载、池隔离 |
|
||||
| 边界条件验证 (M) | 4 | 端口迁移、高端口数量 |
|
||||
| 并发多端口场景 (N) | 3 | 并行 keep-alive、轮询分配 |
|
||||
| 并发多端口场<EFBFBD><EFBFBD>?(N) | 3 | 并行 keep-alive、轮询分<EFBFBD><EFBFBD>?|
|
||||
| **集成测试合计** | **37** | - |
|
||||
| **总计** | **80** | - |
|
||||
|
||||
@@ -195,24 +195,24 @@ xcodebuild test \
|
||||
|
||||
以下测试组涉及复杂的 Mock 场景,可根据需要添加:
|
||||
|
||||
### B. 连接池管理测试 (18个)
|
||||
需要 Mock `HttpdnsNWReusableConnection` 的完整生命周期
|
||||
### B. 连接池管理测<EFBFBD><EFBFBD>?(18<31><38>?
|
||||
需<EFBFBD><EFBFBD>?Mock `HttpdnsNWReusableConnection` 的完整生命周<EFBFBD><EFBFBD>?
|
||||
|
||||
### D. 完整流程测试 (13个)
|
||||
需要 Mock 连接池和网络层的集成场景
|
||||
### D. 完整流程测试 (13<EFBFBD><EFBFBD>?
|
||||
需<EFBFBD><EFBFBD>?Mock 连接池和网络层的集成场景
|
||||
|
||||
## Mock Server 使用
|
||||
|
||||
集成测试使用本地 mock server (127.0.0.1) 替代 httpbin.org,提供稳定可靠的测试环境。
|
||||
集成测试使用本地 mock server (127.0.0.1) 替代 httpbin.org,提供稳定可靠的测试环境<EFBFBD><EFBFBD>?
|
||||
|
||||
### 启动 Mock Server
|
||||
|
||||
```bash
|
||||
cd AlicloudHttpDNSTests/Network
|
||||
cd TrustHttpDNSTests/Network
|
||||
python3 mock_server.py
|
||||
```
|
||||
|
||||
**注意**:使用非特权端口(11080/11443-11446),无需 `sudo` 权限。
|
||||
**注意**:使用非特权端口<EFBFBD><EFBFBD>?1080/11443-11446),无需 `sudo` 权限<EFBFBD><EFBFBD>?
|
||||
|
||||
### 运行集成测试
|
||||
|
||||
@@ -220,38 +220,38 @@ python3 mock_server.py
|
||||
|
||||
```bash
|
||||
xcodebuild test \
|
||||
-workspace AlicloudHttpDNS.xcworkspace \
|
||||
-scheme AlicloudHttpDNSTests \
|
||||
-workspace TrustHttpDNS.xcworkspace \
|
||||
-scheme TrustHttpDNSTests \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
||||
-only-testing:AlicloudHttpDNSTests/HttpdnsNWHTTPClientIntegrationTests
|
||||
-only-testing:TrustHttpDNSTests/HttpdnsNWHTTPClientIntegrationTests
|
||||
```
|
||||
|
||||
### Mock Server 特性
|
||||
### Mock Server 特<EFBFBD><EFBFBD>?
|
||||
|
||||
- **HTTP**: 监听 `127.0.0.1:11080`
|
||||
- **HTTPS**: 监听 `127.0.0.1:11443`, `11444`, `11445`, `11446` (自签名证书)
|
||||
- **多端口目的**: 测试连接池的端口隔离机制
|
||||
- **HTTPS**: 监听 `127.0.0.1:11443`, `11444`, `11445`, `11446` (自签名证<EFBFBD><EFBFBD>?
|
||||
- **多端口目<EFBFBD><EFBFBD>?*: 测试连接池的端口隔离机制
|
||||
- **并发支持**: 多线程处理,适合并发测试
|
||||
- **零延迟**: 本地响应,测试速度快
|
||||
- **零延<EFBFBD><EFBFBD>?*: 本地响应,测试速度<EFBFBD><EFBFBD>?
|
||||
|
||||
详见 [MOCK_SERVER.md](./MOCK_SERVER.md)
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **集成测试依赖 Mock Server**:`HttpdnsNWHTTPClientIntegrationTests` 使用本地 mock server (127.0.0.1)。测试前需先启动 `mock_server.py`。
|
||||
1. **集成测试依赖 Mock Server**:`HttpdnsNWHTTPClientIntegrationTests` 使用本地 mock server (127.0.0.1)。测试前需先启<EFBFBD><EFBFBD>?`mock_server.py`<EFBFBD><EFBFBD>?
|
||||
|
||||
2. **慢测试跳过**:部分测试需要等待31秒(测试连接过期),可设置环境变量 `SKIP_SLOW_TESTS=1` 跳过这些测试:
|
||||
2. **慢测试跳<EFBFBD><EFBFBD>?*:部分测试需要等<EFBFBD><EFBFBD>?1秒(测试连接过期),可设置环境变<EFBFBD><EFBFBD>?`SKIP_SLOW_TESTS=1` 跳过这些测试<EFBFBD><EFBFBD>?
|
||||
- `testRaceCondition_ExpiredConnectionPruning_CreatesNewConnection`
|
||||
- `testConnectionReuse_Expiry31Seconds_NewConnectionCreated`
|
||||
- `testConnectionReuse_TwentyRequestsOneSecondApart_ConnectionKeptAlive`
|
||||
|
||||
3. **并发测试容错**:并发和压力测试允许部分失败(例如 H.4 要求80%成功率),因为高负载下仍可能出现网络波动。
|
||||
3. **并发测试容错**:并发和压力测试允许部分失败(例<EFBFBD><EFBFBD>?H.4 要求80%成功率),因为高负载下仍可能出现网络波动<EFBFBD><EFBFBD>?
|
||||
|
||||
4. **TLS 测试占位符**:E 组 TLS 测试需要真实的 `SecTrustRef` 或高级 mock 框架,当前仅为占位符。
|
||||
4. **TLS 测试占位<EFBFBD><EFBFBD>?*:E <20><>?TLS 测试需要真实的 `SecTrustRef` 或高<EFBFBD><EFBFBD>?mock 框架,当前仅为占位符<EFBFBD><EFBFBD>?
|
||||
|
||||
5. **新文件添加到 Xcode**:创建的测试文件需要手动添加到 `AlicloudHttpDNSTests` target。
|
||||
5. **新文件添加到 Xcode**:创建的测试文件需要手动添加到 `TrustHttpDNSTests` target<EFBFBD><EFBFBD>?
|
||||
|
||||
6. **测试数据**:使用 `HttpdnsNWHTTPClientTestHelper` 生成测试数据,确保测试的可重复性。
|
||||
6. **测试数据**:使<EFBFBD><EFBFBD>?`HttpdnsNWHTTPClientTestHelper` 生成测试数据,确保测试的可重复性<EFBFBD><EFBFBD>?
|
||||
|
||||
## 文件依赖
|
||||
|
||||
@@ -263,20 +263,20 @@ xcodebuild test \
|
||||
|
||||
## 贡献指南
|
||||
|
||||
添加新测试时,请遵循:
|
||||
添加新测试时,请遵循<EFBFBD><EFBFBD>?
|
||||
1. 命名规范:`test<Component>_<Scenario>_<ExpectedResult>`
|
||||
2. 使用 `#pragma mark` 组织测试分组
|
||||
3. 添加清晰的注释说明测试目的
|
||||
4. 验证测试覆盖率并更新本文档
|
||||
3. 添加清晰的注释说明测试目<EFBFBD><EFBFBD>?
|
||||
4. 验证测试覆盖率并更新本文<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2025-11-01
|
||||
**最后更<EFBFBD><EFBFBD>?*: 2025-11-01
|
||||
**测试框架**: XCTest + OCMock
|
||||
**维护者**: Claude Code
|
||||
**维护<EFBFBD><EFBFBD>?*: Claude Code
|
||||
|
||||
**更新日志**:
|
||||
- 2025-11-01: 新增 15 个多端口连接复用测试(K、L、M、N组),测试总数增至 37 个
|
||||
- 2025-11-01: Mock server 新增 3 个 HTTPS 端口(11444-11446)用于测试连接池隔离
|
||||
- 2025-11-01: 新增本地 mock server,替代 httpbin.org,提供稳定测试环境
|
||||
- 2025-11-01: 新增 15 个多端口连接复用测试(K、L、M、N组),测试总数增至 37 <EFBFBD><EFBFBD>?
|
||||
- 2025-11-01: Mock server 新增 3 <EFBFBD><EFBFBD>?HTTPS 端口<EFBFBD><EFBFBD>?1444-11446)用于测试连接池隔离
|
||||
- 2025-11-01: 新增本地 mock server,替<EFBFBD><EFBFBD>?httpbin.org,提供稳定测试环<EFBFBD><EFBFBD>?
|
||||
- 2025-11-01: 新增 15 个并发、竞态和连接复用集成测试(H、I、J组)
|
||||
|
||||
@@ -8,82 +8,82 @@
|
||||
|
||||
## 连接状态机定义
|
||||
|
||||
### 状态属性
|
||||
### 状态属<EFBFBD><EFBFBD>?
|
||||
|
||||
**HttpdnsNWReusableConnection.h:9-11**
|
||||
```objc
|
||||
@property (nonatomic, strong) NSDate *lastUsedDate; // 最后使用时间
|
||||
@property (nonatomic, assign) BOOL inUse; // 是否正在被使用
|
||||
@property (nonatomic, assign, getter=isInvalidated, readonly) BOOL invalidated; // 是否已失效
|
||||
@property (nonatomic, strong) NSDate *lastUsedDate; // 最后使用时<EFBFBD><EFBFBD>?
|
||||
@property (nonatomic, assign) BOOL inUse; // 是否正在被使<EFBFBD><EFBFBD>?
|
||||
@property (nonatomic, assign, getter=isInvalidated, readonly) BOOL invalidated; // 是否已失<EFBFBD><EFBFBD>?
|
||||
```
|
||||
|
||||
### 状态枚举
|
||||
### 状态枚<EFBFBD><EFBFBD>?
|
||||
|
||||
虽然没有显式枚举,但连接实际存在以下逻辑状态:
|
||||
|
||||
| 状态 | `inUse` | `invalidated` | `pool` | 描述 |
|
||||
| 状<EFBFBD><EFBFBD>?| `inUse` | `invalidated` | `pool` | 描述 |
|
||||
|------|---------|---------------|--------|------|
|
||||
| **CREATING** | - | NO | ✗ | 新创建,尚未打开 |
|
||||
| **IN_USE** | YES | NO | ✓ | 已借出,正在使用 |
|
||||
| **IDLE** | NO | NO | ✓ | 空闲,可复用 |
|
||||
| **EXPIRED** | NO | NO | ✓ | 空闲超30秒,待清理 |
|
||||
| **INVALIDATED** | - | YES | ✗ | 已失效,已移除 |
|
||||
| **CREATING** | - | NO | <EFBFBD><EFBFBD>?| 新创建,尚未打开 |
|
||||
| **IN_USE** | YES | NO | <EFBFBD><EFBFBD>?| 已借出,正在使<EFBFBD><EFBFBD>?|
|
||||
| **IDLE** | NO | NO | <EFBFBD><EFBFBD>?| 空闲,可复用 |
|
||||
| **EXPIRED** | NO | NO | <EFBFBD><EFBFBD>?| 空闲<EFBFBD><EFBFBD>?0秒,待清<EFBFBD><EFBFBD>?|
|
||||
| **INVALIDATED** | - | YES | <EFBFBD><EFBFBD>?| 已失效,已移<EFBFBD><EFBFBD>?|
|
||||
|
||||
---
|
||||
|
||||
## 状态转换图
|
||||
|
||||
```
|
||||
┌─────────┐
|
||||
│CREATING │ (new connection)
|
||||
└────┬────┘
|
||||
│ openWithTimeout success
|
||||
▼
|
||||
┌─────────┐
|
||||
│ IN_USE │ (inUse=YES, in pool)
|
||||
└────┬────┘
|
||||
│
|
||||
├──success──► returnConnection(shouldClose=NO)
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌─────────┐
|
||||
│ │ IDLE │ (inUse=NO, in pool)
|
||||
│ └────┬────┘
|
||||
│ │
|
||||
│ ├──dequeue──► IN_USE (reuse)
|
||||
│ │
|
||||
│ ├──idle 30s──► EXPIRED
|
||||
│ │ │
|
||||
│ │ └──prune──► INVALIDATED
|
||||
│ │
|
||||
│ └──!isViable──► INVALIDATED (skip in dequeue)
|
||||
│
|
||||
├──error/timeout──► returnConnection(shouldClose=YES)
|
||||
│ │
|
||||
│ ▼
|
||||
└──────────► ┌──────────────┐
|
||||
│ INVALIDATED │ (removed from pool)
|
||||
└──────────────┘
|
||||
┌─────────<EFBFBD><EFBFBD>?
|
||||
│CREATING <EFBFBD><EFBFBD>?(new connection)
|
||||
└────┬────<EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>?openWithTimeout success
|
||||
<EFBFBD><EFBFBD>?
|
||||
┌─────────<EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>?IN_USE <EFBFBD><EFBFBD>?(inUse=YES, in pool)
|
||||
└────┬────<EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>?
|
||||
├──success──<EFBFBD><EFBFBD>?returnConnection(shouldClose=NO)
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? ┌─────────<EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>? IDLE <EFBFBD><EFBFBD>?(inUse=NO, in pool)
|
||||
<EFBFBD><EFBFBD>? └────┬────<EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? ├──dequeue──<EFBFBD><EFBFBD>?IN_USE (reuse)
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? ├──idle 30s──<EFBFBD><EFBFBD>?EXPIRED
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>? └──prune──<EFBFBD><EFBFBD>?INVALIDATED
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? └──!isViable──<EFBFBD><EFBFBD>?INVALIDATED (skip in dequeue)
|
||||
<EFBFBD><EFBFBD>?
|
||||
├──error/timeout──<EFBFBD><EFBFBD>?returnConnection(shouldClose=YES)
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>? <EFBFBD><EFBFBD>?
|
||||
└──────────<EFBFBD><EFBFBD>?┌──────────────<EFBFBD><EFBFBD>?
|
||||
<EFBFBD><EFBFBD>?INVALIDATED <EFBFBD><EFBFBD>?(removed from pool)
|
||||
└──────────────<EFBFBD><EFBFBD>?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 代码中的状态转换
|
||||
## 代码中的状态转<EFBFBD><EFBFBD>?
|
||||
|
||||
### 1. CREATING → IN_USE (新连接)
|
||||
### 1. CREATING <EFBFBD><EFBFBD>?IN_USE (新连<EFBFBD><EFBFBD>?
|
||||
|
||||
**HttpdnsNWHTTPClient.m:248-249**
|
||||
```objc
|
||||
newConnection.inUse = YES;
|
||||
newConnection.lastUsedDate = now;
|
||||
[pool addObject:newConnection]; // 加入池
|
||||
[pool addObject:newConnection]; // 加入<EFBFBD><EFBFBD>?
|
||||
```
|
||||
|
||||
**何时触发:**
|
||||
- `dequeueConnectionForHost` 找不到可复用连接
|
||||
- 创建新连接并成功打开
|
||||
|
||||
### 2. IDLE → IN_USE (复用)
|
||||
### 2. IDLE <EFBFBD><EFBFBD>?IN_USE (复用)
|
||||
|
||||
**HttpdnsNWHTTPClient.m:210-214**
|
||||
```objc
|
||||
@@ -97,16 +97,16 @@ for (HttpdnsNWReusableConnection *candidate in pool) {
|
||||
}
|
||||
```
|
||||
|
||||
**关键检查:**
|
||||
- `!candidate.inUse` - 必须是空闲状态
|
||||
**关键检<EFBFBD><EFBFBD>?**
|
||||
- `!candidate.inUse` - 必须是空闲状<EFBFBD><EFBFBD>?
|
||||
- `[candidate isViable]` - 连接必须仍然有效
|
||||
|
||||
### 3. IN_USE → IDLE (正常归还)
|
||||
### 3. IN_USE <EFBFBD><EFBFBD>?IDLE (正常归还)
|
||||
|
||||
**HttpdnsNWHTTPClient.m:283-288**
|
||||
```objc
|
||||
if (shouldClose || connection.isInvalidated) {
|
||||
// → INVALIDATED (见#4)
|
||||
// <EFBFBD><EFBFBD>?INVALIDATED (<EFBFBD><EFBFBD>?4)
|
||||
} else {
|
||||
connection.inUse = NO;
|
||||
connection.lastUsedDate = now;
|
||||
@@ -120,7 +120,7 @@ if (shouldClose || connection.isInvalidated) {
|
||||
**防护措施:**
|
||||
- Line 285: `if (![pool containsObject:connection])` - 防止重复添加
|
||||
|
||||
### 4. IN_USE/IDLE → INVALIDATED (失效)
|
||||
### 4. IN_USE/IDLE <EFBFBD><EFBFBD>?INVALIDATED (失效)
|
||||
|
||||
**HttpdnsNWHTTPClient.m:279-281**
|
||||
```objc
|
||||
@@ -132,9 +132,9 @@ if (shouldClose || connection.isInvalidated) {
|
||||
|
||||
**触发条件:**
|
||||
- `shouldClose=YES` (timeout, error, parse failure, remote close)
|
||||
- `connection.isInvalidated=YES` (连接已失效)
|
||||
- `connection.isInvalidated=YES` (连接已失<EFBFBD><EFBFBD>?
|
||||
|
||||
### 5. EXPIRED → INVALIDATED (过期清理)
|
||||
### 5. EXPIRED <EFBFBD><EFBFBD>?INVALIDATED (过期清理)
|
||||
|
||||
**HttpdnsNWHTTPClient.m:297-312**
|
||||
```objc
|
||||
@@ -145,7 +145,7 @@ if (shouldClose || connection.isInvalidated) {
|
||||
if (conn.inUse) continue; // 跳过使用中的
|
||||
|
||||
NSTimeInterval idle = [referenceDate timeIntervalSinceDate:conn.lastUsedDate];
|
||||
if (idle > kHttpdnsNWHTTPClientConnectionIdleTimeout) { // 30秒
|
||||
if (idle > kHttpdnsNWHTTPClientConnectionIdleTimeout) { // 30<EFBFBD><EFBFBD>?
|
||||
[toRemove addObject:conn];
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ if (shouldClose || connection.isInvalidated) {
|
||||
[pool removeObject:conn];
|
||||
}
|
||||
|
||||
// 限制池大小 ≤ 4
|
||||
// 限制池大<EFBFBD><EFBFBD>?<3F><>?4
|
||||
while (pool.count > kHttpdnsNWHTTPClientMaxIdleConnectionsPerHost) {
|
||||
HttpdnsNWReusableConnection *oldest = pool.firstObject;
|
||||
[oldest invalidate];
|
||||
@@ -168,72 +168,72 @@ if (shouldClose || connection.isInvalidated) {
|
||||
|
||||
## 当前测试覆盖情况
|
||||
|
||||
### ✅ 已测试的正常流程
|
||||
### <EFBFBD><EFBFBD>?已测试的正常流程
|
||||
|
||||
| 状态转换 | 测试 | 覆盖 |
|
||||
| 状态转<EFBFBD><EFBFBD>?| 测试 | 覆盖 |
|
||||
|----------|------|------|
|
||||
| CREATING → IN_USE → IDLE | G.1-G.7, O.1 | ✅ |
|
||||
| IDLE → IN_USE (复用) | G.2, O.1-O.3, J.1-J.5 | ✅ |
|
||||
| IN_USE → INVALIDATED (timeout) | P.1-P.6 | ✅ |
|
||||
| EXPIRED → INVALIDATED (30s) | J.2, M.4, I.4 | ✅ |
|
||||
| 池容量限制 (max 4) | O.3, J.3 | ✅ |
|
||||
| 并发状态访问 | I.1-I.5, M.3 | ✅ |
|
||||
| CREATING <EFBFBD><EFBFBD>?IN_USE <EFBFBD><EFBFBD>?IDLE | G.1-G.7, O.1 | <EFBFBD><EFBFBD>?|
|
||||
| IDLE <EFBFBD><EFBFBD>?IN_USE (复用) | G.2, O.1-O.3, J.1-J.5 | <EFBFBD><EFBFBD>?|
|
||||
| IN_USE <EFBFBD><EFBFBD>?INVALIDATED (timeout) | P.1-P.6 | <EFBFBD><EFBFBD>?|
|
||||
| EXPIRED <EFBFBD><EFBFBD>?INVALIDATED (30s) | J.2, M.4, I.4 | <EFBFBD><EFBFBD>?|
|
||||
| 池容量限<EFBFBD><EFBFBD>?(max 4) | O.3, J.3 | <EFBFBD><EFBFBD>?|
|
||||
| 并发状态访<EFBFBD><EFBFBD>?| I.1-I.5, M.3 | <EFBFBD><EFBFBD>?|
|
||||
|
||||
### ❌ 未测试的异常场景
|
||||
### <EFBFBD><EFBFBD>?未测试的异常场景
|
||||
|
||||
#### 1. **连接在池中失效(Stale Connection)**
|
||||
#### 1. **连接在池中失效(Stale Connection<EFBFBD><EFBFBD>?*
|
||||
|
||||
**场景:**
|
||||
- 连接空闲 29 秒(未到 30 秒过期)
|
||||
- 服务器主动关闭连接
|
||||
- `dequeue` 时 `isViable` 返回 NO
|
||||
- 服务器主动关闭连<EFBFBD><EFBFBD>?
|
||||
- `dequeue` <EFBFBD><EFBFBD>?`isViable` 返回 NO
|
||||
|
||||
**当前代码行为:**
|
||||
```objc
|
||||
for (HttpdnsNWReusableConnection *candidate in pool) {
|
||||
if (!candidate.inUse && [candidate isViable]) { // ← isViable 检查
|
||||
// 只复用有效连接
|
||||
if (!candidate.inUse && [candidate isViable]) { // <EFBFBD><EFBFBD>?isViable 检<EFBFBD><EFBFBD>?
|
||||
// 只复用有效连<EFBFBD><EFBFBD>?
|
||||
}
|
||||
}
|
||||
// 如果所有连接都 !isViable,会创建新连接
|
||||
// 如果所有连接都 !isViable,会创建新连<EFBFBD><EFBFBD>?
|
||||
```
|
||||
|
||||
**风险:** 未验证 `isViable` 检查是否真的工作
|
||||
**风险:** 未验<EFBFBD><EFBFBD>?`isViable` 检查是否真的工<EFBFBD><EFBFBD>?
|
||||
|
||||
**测试需求:** Q.1
|
||||
**测试需<EFBFBD><EFBFBD>?** Q.1
|
||||
```objc
|
||||
testStateTransition_StaleConnectionInPool_SkipsAndCreatesNew
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2. **双重归还(Double Return)**
|
||||
#### 2. **双重归还(Double Return<EFBFBD><EFBFBD>?*
|
||||
|
||||
**场景:**
|
||||
- 连接被归还
|
||||
- 连接被归<EFBFBD><EFBFBD>?
|
||||
- 代码错误,再次归还同一连接
|
||||
|
||||
**当前代码防护:**
|
||||
```objc
|
||||
if (![pool containsObject:connection]) {
|
||||
[pool addObject:connection]; // ← 防止重复添加
|
||||
[pool addObject:connection]; // <EFBFBD><EFBFBD>?防止重复添加
|
||||
}
|
||||
```
|
||||
|
||||
**风险:** 未验证防护是否有效
|
||||
**风险:** 未验证防护是否有<EFBFBD><EFBFBD>?
|
||||
|
||||
**测试需求:** Q.2
|
||||
**测试需<EFBFBD><EFBFBD>?** Q.2
|
||||
```objc
|
||||
testStateTransition_DoubleReturn_Idempotent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3. **归还错误的池键(Wrong Pool Key)**
|
||||
#### 3. **归还错误的池键(Wrong Pool Key<EFBFBD><EFBFBD>?*
|
||||
|
||||
**场景:**
|
||||
- 从池A借出连接
|
||||
- 归还到池B(错误的key)
|
||||
- 归还到池B(错误的key<EFBFBD><EFBFBD>?
|
||||
|
||||
**当前代码行为:**
|
||||
```objc
|
||||
@@ -246,9 +246,9 @@ testStateTransition_DoubleReturn_Idempotent
|
||||
}
|
||||
```
|
||||
|
||||
**风险:** 可能导致池污染
|
||||
**风险:** 可能导致池污<EFBFBD><EFBFBD>?
|
||||
|
||||
**测试需求:** Q.3
|
||||
**测试需<EFBFBD><EFBFBD>?** Q.3
|
||||
```objc
|
||||
testStateTransition_ReturnToWrongPool_Isolated
|
||||
```
|
||||
@@ -259,67 +259,67 @@ testStateTransition_ReturnToWrongPool_Isolated
|
||||
|
||||
**场景:**
|
||||
- 连接被借出 (inUse=YES)
|
||||
- `sendRequestData` 过程中网络错误
|
||||
- 连接被标记 invalidated
|
||||
- `sendRequestData` 过程中网络错<EFBFBD><EFBFBD>?
|
||||
- 连接被标<EFBFBD><EFBFBD>?invalidated
|
||||
|
||||
**当前代码行为:**
|
||||
```objc
|
||||
NSData *rawResponse = [connection sendRequestData:requestData ...];
|
||||
if (!rawResponse) {
|
||||
[self returnConnection:connection forKey:poolKey shouldClose:YES]; // ← invalidated
|
||||
[self returnConnection:connection forKey:poolKey shouldClose:YES]; // <EFBFBD><EFBFBD>?invalidated
|
||||
}
|
||||
```
|
||||
|
||||
**测试需求:** Q.4
|
||||
**测试需<EFBFBD><EFBFBD>?** Q.4
|
||||
```objc
|
||||
testStateTransition_ErrorDuringUse_Invalidated
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 5. **池容量超限时的移除策略**
|
||||
#### 5. **池容量超限时的移除策<EFBFBD><EFBFBD>?*
|
||||
|
||||
**场景:**
|
||||
- 池已有 4 个连接
|
||||
- 第 5 个连接被归还
|
||||
- 池已<EFBFBD><EFBFBD>?4 个连<EFBFBD><EFBFBD>?
|
||||
- <EFBFBD><EFBFBD>?5 个连接被归还
|
||||
|
||||
**当前代码行为:**
|
||||
```objc
|
||||
while (pool.count > kHttpdnsNWHTTPClientMaxIdleConnectionsPerHost) {
|
||||
HttpdnsNWReusableConnection *oldest = pool.firstObject; // ← 移除最老的
|
||||
HttpdnsNWReusableConnection *oldest = pool.firstObject; // <EFBFBD><EFBFBD>?移除最老的
|
||||
[oldest invalidate];
|
||||
[pool removeObject:oldest];
|
||||
}
|
||||
```
|
||||
|
||||
**问题:**
|
||||
- 移除 `pool.firstObject` - 是按添加顺序还是使用顺序?
|
||||
- 移除 `pool.firstObject` - 是按添加顺序还是使用顺序<EFBFBD><EFBFBD>?
|
||||
- NSMutableArray 顺序是否能保证?
|
||||
|
||||
**测试需求:** Q.5
|
||||
**测试需<EFBFBD><EFBFBD>?** Q.5
|
||||
```objc
|
||||
testStateTransition_PoolOverflow_RemovesOldest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6. **并发状态竞态**
|
||||
#### 6. **并发状态竞<EFBFBD><EFBFBD>?*
|
||||
|
||||
**场景:**
|
||||
- Thread A: dequeue 连接,设置 `inUse=YES`
|
||||
- Thread A: dequeue 连接,设<EFBFBD><EFBFBD>?`inUse=YES`
|
||||
- Thread B: 同时 prune 过期连接
|
||||
- 竞态:连接同时被标记 inUse 和被移除
|
||||
- 竞态:连接同时被标<EFBFBD><EFBFBD>?inUse 和被移除
|
||||
|
||||
**当前代码防护:**
|
||||
```objc
|
||||
- (void)pruneConnectionPool:... {
|
||||
for (HttpdnsNWReusableConnection *conn in pool) {
|
||||
if (conn.inUse) continue; // ← 跳过使用中的
|
||||
if (conn.inUse) continue; // <EFBFBD><EFBFBD>?跳过使用中的
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**测试需求:** Q.6 (可能已被 I 组部分覆盖)
|
||||
**测试需<EFBFBD><EFBFBD>?** Q.6 (可能已被 I 组部分覆<EFBFBD><EFBFBD>?
|
||||
```objc
|
||||
testStateTransition_ConcurrentDequeueAndPrune_NoCorruption
|
||||
```
|
||||
@@ -335,100 +335,100 @@ testStateTransition_ConcurrentDequeueAndPrune_NoCorruption
|
||||
**当前代码行为:**
|
||||
```objc
|
||||
if (![newConnection openWithTimeout:timeout error:error]) {
|
||||
[newConnection invalidate]; // ← 立即失效
|
||||
return nil; // ← 不加入池
|
||||
[newConnection invalidate]; // <EFBFBD><EFBFBD>?立即失效
|
||||
return nil; // <EFBFBD><EFBFBD>?不加入池
|
||||
}
|
||||
```
|
||||
|
||||
**测试需求:** Q.7
|
||||
**测试需<EFBFBD><EFBFBD>?** Q.7
|
||||
```objc
|
||||
testStateTransition_OpenFails_NotAddedToPool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 状态不变式(State Invariants)
|
||||
## 状态不变式(State Invariants<EFBFBD><EFBFBD>?
|
||||
|
||||
### 应该始终成立的约束
|
||||
### 应该始终成立的约<EFBFBD><EFBFBD>?
|
||||
|
||||
1. **互斥性:**
|
||||
1. **互斥<EFBFBD><EFBFBD>?**
|
||||
```
|
||||
∀ connection: (inUse=YES) ⇒ (dequeue count ≤ 1)
|
||||
∀ connection: (inUse=YES) <EFBFBD><EFBFBD>?(dequeue count <EFBFBD><EFBFBD>?1)
|
||||
```
|
||||
同一连接不能被多次借出
|
||||
|
||||
2. **池完整性:**
|
||||
2. **池完整<EFBFBD><EFBFBD>?**
|
||||
```
|
||||
∀ pool: ∑(connections) ≤ maxPoolSize (4)
|
||||
∀ pool: <EFBFBD><EFBFBD>?connections) <EFBFBD><EFBFBD>?maxPoolSize (4)
|
||||
```
|
||||
每个池最多 4 个连接
|
||||
每个池最<EFBFBD><EFBFBD>?4 个连<EFBFBD><EFBFBD>?
|
||||
|
||||
3. **状态一致性:**
|
||||
3. **状态一致<EFBFBD><EFBFBD>?**
|
||||
```
|
||||
∀ connection in pool: !invalidated
|
||||
```
|
||||
池中不应有失效连接
|
||||
池中不应有失效连<EFBFBD><EFBFBD>?
|
||||
|
||||
4. **时间单调性:**
|
||||
4. **时间单调<EFBFBD><EFBFBD>?**
|
||||
```
|
||||
∀ connection: lastUsedDate 随每次使用递增
|
||||
```
|
||||
|
||||
5. **失效不可逆:**
|
||||
5. **失效不可<EFBFBD><EFBFBD>?**
|
||||
```
|
||||
invalidated=YES ⇒ connection removed from pool
|
||||
invalidated=YES <EFBFBD><EFBFBD>?connection removed from pool
|
||||
```
|
||||
失效连接必须从池中移除
|
||||
失效连接必须从池中移<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
## 测试设计建议
|
||||
|
||||
### Q 组:状态机异常转换测试(7个新测试)
|
||||
### Q 组:状态机异常转换测试<EFBFBD><EFBFBD>?个新测试<EFBFBD><EFBFBD>?
|
||||
|
||||
| 测试 | 验证内容 | 难度 |
|
||||
|------|---------|------|
|
||||
| **Q.1** | Stale connection 被 `isViable` 检测并跳过 | 🔴 高(需要模拟服务器关闭) |
|
||||
| **Q.2** | 双重归还是幂等的 | 🟢 低 |
|
||||
| **Q.3** | 归还到错误池键不污染其他池 | 🟡 中 |
|
||||
| **Q.4** | 使用中错误导致连接失效 | 🟢 低(已有 P 组部分覆盖) |
|
||||
| **Q.5** | 池溢出时移除最旧连接 | 🟡 中 |
|
||||
| **Q.6** | 并发 dequeue/prune 竞态 | 🔴 高(需要精确时序) |
|
||||
| **Q.7** | 打开失败的连接不加入池 | 🟢 低 |
|
||||
| **Q.1** | Stale connection <EFBFBD><EFBFBD>?`isViable` 检测并跳过 | 🔴 高(需要模拟服务器关闭<EFBFBD><EFBFBD>?|
|
||||
| **Q.2** | 双重归还是幂等的 | 🟢 <EFBFBD><EFBFBD>?|
|
||||
| **Q.3** | 归还到错误池键不污染其他<EFBFBD><EFBFBD>?| 🟡 <EFBFBD><EFBFBD>?|
|
||||
| **Q.4** | 使用中错误导致连接失<EFBFBD><EFBFBD>?| 🟢 低(已有 P 组部分覆盖) |
|
||||
| **Q.5** | 池溢出时移除最旧连<EFBFBD><EFBFBD>?| 🟡 <EFBFBD><EFBFBD>?|
|
||||
| **Q.6** | 并发 dequeue/prune 竞<EFBFBD><EFBFBD>?| 🔴 高(需要精确时序) |
|
||||
| **Q.7** | 打开失败的连接不加入<EFBFBD><EFBFBD>?| 🟢 <EFBFBD><EFBFBD>?|
|
||||
|
||||
---
|
||||
|
||||
## 状态机验证策略
|
||||
|
||||
### 方法1: 直接状态检查
|
||||
### 方法1: 直接状态检<EFBFBD><EFBFBD>?
|
||||
|
||||
```objc
|
||||
// 验证状态属性
|
||||
// 验证状态属<EFBFBD><EFBFBD>?
|
||||
XCTAssertTrue(connection.inUse);
|
||||
XCTAssertFalse(connection.isInvalidated);
|
||||
XCTAssertEqual([poolCount], expectedCount);
|
||||
```
|
||||
|
||||
### 方法2: 状态转换序列
|
||||
### 方法2: 状态转换序<EFBFBD><EFBFBD>?
|
||||
|
||||
```objc
|
||||
// 验证转换序列
|
||||
[client resetPoolStatistics];
|
||||
|
||||
// CREATING → IN_USE
|
||||
// CREATING <EFBFBD><EFBFBD>?IN_USE
|
||||
response1 = [client performRequest...];
|
||||
XCTAssertEqual(creationCount, 1);
|
||||
|
||||
// IN_USE → IDLE
|
||||
// IN_USE <EFBFBD><EFBFBD>?IDLE
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
XCTAssertEqual(poolCount, 1);
|
||||
|
||||
// IDLE → IN_USE (reuse)
|
||||
// IDLE <EFBFBD><EFBFBD>?IN_USE (reuse)
|
||||
response2 = [client performRequest...];
|
||||
XCTAssertEqual(reuseCount, 1);
|
||||
```
|
||||
|
||||
### 方法3: 不变式验证
|
||||
### 方法3: 不变式验<EFBFBD><EFBFBD>?
|
||||
|
||||
```objc
|
||||
// 验证池不变式
|
||||
@@ -441,32 +441,32 @@ for (NSString *key in keys) {
|
||||
|
||||
---
|
||||
|
||||
## 当前覆盖率评估
|
||||
## 当前覆盖率评<EFBFBD><EFBFBD>?
|
||||
|
||||
### 状态转换覆盖矩阵
|
||||
### 状态转换覆盖矩<EFBFBD><EFBFBD>?
|
||||
|
||||
| From ↓ / To → | CREATING | IN_USE | IDLE | EXPIRED | INVALIDATED |
|
||||
| From <EFBFBD><EFBFBD>?/ To <EFBFBD><EFBFBD>?| CREATING | IN_USE | IDLE | EXPIRED | INVALIDATED |
|
||||
|---------------|----------|--------|------|---------|-------------|
|
||||
| **CREATING** | - | ✅ | ❌ | ❌ | ✅ (Q.7 needed) |
|
||||
| **IN_USE** | ❌ | - | ✅ | ❌ | ✅ |
|
||||
| **IDLE** | ❌ | ✅ | - | ✅ | ❌ (Q.1 needed) |
|
||||
| **EXPIRED** | ❌ | ❌ | ❌ | - | ✅ |
|
||||
| **INVALIDATED** | ❌ | ❌ | ❌ | ❌ | - |
|
||||
| **CREATING** | - | <EFBFBD><EFBFBD>?| <20><>?| <20><>?| <20><>?(Q.7 needed) |
|
||||
| **IN_USE** | <EFBFBD><EFBFBD>?| - | <EFBFBD><EFBFBD>?| <20><>?| <20><>?|
|
||||
| **IDLE** | <EFBFBD><EFBFBD>?| <20><>?| - | <20><>?| <20><>?(Q.1 needed) |
|
||||
| **EXPIRED** | <EFBFBD><EFBFBD>?| <20><>?| <20><>?| - | <EFBFBD><EFBFBD>?|
|
||||
| **INVALIDATED** | <EFBFBD><EFBFBD>?| <20><>?| <20><>?| <20><>?| - |
|
||||
|
||||
**覆盖率:** 6/25 transitions = 24%
|
||||
**有效覆盖率:** 6/10 valid transitions = 60%
|
||||
**覆盖<EFBFBD><EFBFBD>?** 6/25 transitions = 24%
|
||||
**有效覆盖<EFBFBD><EFBFBD>?** 6/10 valid transitions = 60%
|
||||
|
||||
### 异常场景覆盖
|
||||
|
||||
| 异常场景 | 当前测试 | 覆盖 |
|
||||
|----------|---------|------|
|
||||
| Stale connection | ❌ | 0% |
|
||||
| Double return | ❌ | 0% |
|
||||
| Wrong pool key | ❌ | 0% |
|
||||
| Stale connection | <EFBFBD><EFBFBD>?| 0% |
|
||||
| Double return | <EFBFBD><EFBFBD>?| 0% |
|
||||
| Wrong pool key | <EFBFBD><EFBFBD>?| 0% |
|
||||
| Error during use | P.1-P.6 | 100% |
|
||||
| Pool overflow | O.3, J.3 | 50% (未验证移除策略) |
|
||||
| Pool overflow | O.3, J.3 | 50% (未验证移除策<EFBFBD><EFBFBD>? |
|
||||
| Concurrent race | I.1-I.5 | 80% |
|
||||
| Open failure | ❌ | 0% |
|
||||
| Open failure | <EFBFBD><EFBFBD>?| 0% |
|
||||
|
||||
**总体异常覆盖:** ~40%
|
||||
|
||||
@@ -476,15 +476,15 @@ for (NSString *key in keys) {
|
||||
|
||||
### 高风险未测试场景
|
||||
|
||||
**风险等级 🔴 高:**
|
||||
**风险等级 🔴 <EFBFBD><EFBFBD>?**
|
||||
1. **Stale Connection (Q.1)** - 可能导致请求失败
|
||||
2. **Concurrent Dequeue/Prune (Q.6)** - 可能导致状态不一致
|
||||
2. **Concurrent Dequeue/Prune (Q.6)** - 可能导致状态不一<EFBFBD><EFBFBD>?
|
||||
|
||||
**风险等级 🟡 中:**
|
||||
3. **Wrong Pool Key (Q.3)** - 可能导致池污染
|
||||
**风险等级 🟡 <EFBFBD><EFBFBD>?**
|
||||
3. **Wrong Pool Key (Q.3)** - 可能导致池污<EFBFBD><EFBFBD>?
|
||||
4. **Pool Overflow Strategy (Q.5)** - LRU vs FIFO 影响性能
|
||||
|
||||
**风险等级 🟢 低:**
|
||||
**风险等级 🟢 <EFBFBD><EFBFBD>?**
|
||||
5. **Double Return (Q.2)** - 已有代码防护
|
||||
6. **Open Failure (Q.7)** - 已有错误处理
|
||||
|
||||
@@ -494,21 +494,21 @@ for (NSString *key in keys) {
|
||||
|
||||
### 短期(关键)
|
||||
|
||||
1. ✅ **添加 Q.2 测试** - 验证双重归还防护
|
||||
2. ✅ **添加 Q.5 测试** - 验证池溢出移除策略
|
||||
3. ✅ **添加 Q.7 测试** - 验证打开失败处理
|
||||
1. <EFBFBD><EFBFBD>?**添加 Q.2 测试** - 验证双重归还防护
|
||||
2. <EFBFBD><EFBFBD>?**添加 Q.5 测试** - 验证池溢出移除策<EFBFBD><EFBFBD>?
|
||||
3. <EFBFBD><EFBFBD>?**添加 Q.7 测试** - 验证打开失败处理
|
||||
|
||||
### 中期(增强)
|
||||
|
||||
4. ⚠️ **添加 Q.3 测试** - 验证池隔离
|
||||
5. ⚠️ **添加 Q.1 测试** - 验证 stale connection(需要 mock)
|
||||
4. ⚠️ **添加 Q.3 测试** - 验证池隔<EFBFBD><EFBFBD>?
|
||||
5. ⚠️ **添加 Q.1 测试** - 验证 stale connection(需<EFBFBD><EFBFBD>?mock<EFBFBD><EFBFBD>?
|
||||
|
||||
### 长期(完整)
|
||||
|
||||
6. 🔬 **添加 Q.6 测试** - 验证并发竞态(复杂)
|
||||
6. 🔬 **添加 Q.6 测试** - 验证并发竞态(复杂<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
**创建时间**: 2025-11-01
|
||||
**作者**: Claude Code
|
||||
**状态**: 分析完成,待实现 Q 组测试
|
||||
**作<EFBFBD><EFBFBD>?*: Claude Code
|
||||
**状<EFBFBD><EFBFBD>?*: 分析完成,待实现 Q 组测<EFBFBD><EFBFBD>?
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
## 问题描述
|
||||
|
||||
当前测试套件没有充分验证**超时与连接池交互**的"无形结果"(intangible outcomes),可能存在以下风险:
|
||||
当前测试套件没有充分验证**超时与连接池交互**<EFBFBD><EFBFBD>?无形结果"(intangible outcomes),可能存在以下风险<EFBFBD><EFBFBD>?
|
||||
- 超时后的连接泄漏
|
||||
- 连接池被超时连接污染
|
||||
- 连接池无法从超时中恢复
|
||||
- 连接池无法从超时中恢<EFBFBD><EFBFBD>?
|
||||
- 并发场景下部分超时影响整体池健康
|
||||
|
||||
---
|
||||
@@ -26,11 +26,11 @@ if (!rawResponse) {
|
||||
```objc
|
||||
if (shouldClose || connection.isInvalidated) {
|
||||
[connection invalidate]; // 取消底层 nw_connection
|
||||
[pool removeObject:connection]; // 从池中移除
|
||||
[pool removeObject:connection]; // 从池中移<EFBFBD><EFBFBD>?
|
||||
}
|
||||
```
|
||||
|
||||
**结论**:代码逻辑正确,超时连接**会被移除**而非留在池中。
|
||||
**结论**:代码逻辑正确,超时连<EFBFBD><EFBFBD>?*会被移除**而非留在池中<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
@@ -38,33 +38,33 @@ if (shouldClose || connection.isInvalidated) {
|
||||
|
||||
### 已有测试:`testIntegration_RequestTimeout_ReturnsError`
|
||||
|
||||
**验证内容:**
|
||||
- ✅ 超时返回 `nil` response
|
||||
- ✅ 超时设置 `error`
|
||||
**验证内容<EFBFBD><EFBFBD>?*
|
||||
- <EFBFBD><EFBFBD>?超时返回 `nil` response
|
||||
- <EFBFBD><EFBFBD>?超时设置 `error`
|
||||
|
||||
**未验证内容(缺失):**
|
||||
- ❌ 连接是否从池中移除
|
||||
- ❌ 池计数是否正确
|
||||
- ❌ 后续请求是否正常工作
|
||||
- ❌ 是否存在连接泄漏
|
||||
- ❌ 并发场景下部分超时的影响
|
||||
- <EFBFBD><EFBFBD>?连接是否从池中移<EFBFBD><EFBFBD>?
|
||||
- <EFBFBD><EFBFBD>?池计数是否正<EFBFBD><EFBFBD>?
|
||||
- <EFBFBD><EFBFBD>?后续请求是否正常工作
|
||||
- <EFBFBD><EFBFBD>?是否存在连接泄漏
|
||||
- <EFBFBD><EFBFBD>?并发场景下部分超时的影响
|
||||
|
||||
---
|
||||
|
||||
## 需要验证的"无形结果"
|
||||
|
||||
### 1. 单次超时后的池清理
|
||||
### 1. 单次超时后的池清<EFBFBD><EFBFBD>?
|
||||
|
||||
**场景**:
|
||||
1. 请求 A 超时(timeout=1s, endpoint=/delay/10)
|
||||
2. 验证池状态
|
||||
**场景**<EFBFBD><EFBFBD>?
|
||||
1. 请求 A 超时(timeout=1s, endpoint=/delay/10<EFBFBD><EFBFBD>?
|
||||
2. 验证池状<EFBFBD><EFBFBD>?
|
||||
|
||||
**应验证:**
|
||||
- Pool count = 0(连接已移除)
|
||||
- Pool count = 0(连接已移除<EFBFBD><EFBFBD>?
|
||||
- Total connection count 没有异常增长
|
||||
- 无连接泄漏
|
||||
- 无连接泄<EFBFBD><EFBFBD>?
|
||||
|
||||
**测试方法**:
|
||||
**测试方法**<EFBFBD><EFBFBD>?
|
||||
```objc
|
||||
[client resetPoolStatistics];
|
||||
|
||||
@@ -78,7 +78,7 @@ HttpdnsNWHTTPClientResponse *response = [client performRequestWithURLString:@"ht
|
||||
XCTAssertNil(response);
|
||||
XCTAssertNotNil(error);
|
||||
|
||||
// 验证池状态
|
||||
// 验证池状<EFBFBD><EFBFBD>?
|
||||
NSString *poolKey = @"127.0.0.1:11080:tcp";
|
||||
XCTAssertEqual([client connectionPoolCountForKey:poolKey], 0, @"Timed-out connection should be removed");
|
||||
XCTAssertEqual([client totalConnectionCount], 0, @"No connections should remain");
|
||||
@@ -88,72 +88,72 @@ XCTAssertEqual(client.connectionReuseCount, 0, @"No reuse for timed-out connecti
|
||||
|
||||
---
|
||||
|
||||
### 2. 超时后的池恢复能力
|
||||
### 2. 超时后的池恢复能<EFBFBD><EFBFBD>?
|
||||
|
||||
**场景**:
|
||||
**场景**<EFBFBD><EFBFBD>?
|
||||
1. 请求 A 超时
|
||||
2. 请求 B 正常(验证池恢复)
|
||||
3. 请求 C 复用 B 的连接
|
||||
2. 请求 B 正常(验证池恢复<EFBFBD><EFBFBD>?
|
||||
3. 请求 C 复用 B 的连<EFBFBD><EFBFBD>?
|
||||
|
||||
**应验证:**
|
||||
- 请求 B 成功(池已恢复)
|
||||
- 请求 C 复用连接(connectionReuseCount = 1)
|
||||
- Pool count = 1(只有 B/C 的连接)
|
||||
- 请求 C 复用连接(connectionReuseCount = 1<EFBFBD><EFBFBD>?
|
||||
- Pool count = 1(只<EFBFBD><EFBFBD>?B/C 的连接)
|
||||
|
||||
---
|
||||
|
||||
### 3. 并发场景:部分超时不影响成功请求
|
||||
|
||||
**场景**:
|
||||
1. 并发发起 10 个请求
|
||||
2. 5 个正常(timeout=15s)
|
||||
3. 5 个超时(timeout=0.5s, endpoint=/delay/10)
|
||||
**场景**<EFBFBD><EFBFBD>?
|
||||
1. 并发发起 10 个请<EFBFBD><EFBFBD>?
|
||||
2. 5 个正常(timeout=15s<EFBFBD><EFBFBD>?
|
||||
3. 5 个超时(timeout=0.5s, endpoint=/delay/10<EFBFBD><EFBFBD>?
|
||||
|
||||
**应验证:**
|
||||
- 5 个正常请求成功
|
||||
- 5 个超时请求失败
|
||||
- Pool count ≤ 5(只保留成功的连接)
|
||||
- Total connection count ≤ 5(无泄漏)
|
||||
- connectionCreationCount ≤ 10(合理范围)
|
||||
- 成功的请求可以复用连接
|
||||
- 5 个正常请求成<EFBFBD><EFBFBD>?
|
||||
- 5 个超时请求失<EFBFBD><EFBFBD>?
|
||||
- Pool count <EFBFBD><EFBFBD>?5(只保留成功的连接)
|
||||
- Total connection count <EFBFBD><EFBFBD>?5(无泄漏<EFBFBD><EFBFBD>?
|
||||
- connectionCreationCount <EFBFBD><EFBFBD>?10(合理范围)
|
||||
- 成功的请求可以复用连<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
### 4. 连续超时不导致资源泄漏
|
||||
### 4. 连续超时不导致资源泄<EFBFBD><EFBFBD>?
|
||||
|
||||
**场景**:
|
||||
1. 连续 20 次超时请求
|
||||
2. 验证连接池没有累积"僵尸连接"
|
||||
**场景**<EFBFBD><EFBFBD>?
|
||||
1. 连续 20 次超时请<EFBFBD><EFBFBD>?
|
||||
2. 验证连接池没有累<EFBFBD><EFBFBD>?僵尸连接"
|
||||
|
||||
**应验证:**
|
||||
- Pool count = 0
|
||||
- Total connection count = 0
|
||||
- connectionCreationCount = 20(每次都创建新连接,因为超时的被移除)
|
||||
- connectionCreationCount = 20(每次都创建新连接,因为超时的被移除<EFBFBD><EFBFBD>?
|
||||
- connectionReuseCount = 0(超时连接不可复用)
|
||||
- 无内存泄漏(虽然代码层面无法直接测试)
|
||||
- 无内存泄漏(虽然代码层面无法直接测试<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
### 5. 超时不阻塞连接池
|
||||
|
||||
**场景**:
|
||||
1. 请求 A 超时(endpoint=/delay/10, timeout=1s)
|
||||
2. 同时请求 B 正常(endpoint=/get, timeout=15s)
|
||||
**场景**<EFBFBD><EFBFBD>?
|
||||
1. 请求 A 超时(endpoint=/delay/10, timeout=1s<EFBFBD><EFBFBD>?
|
||||
2. 同时请求 B 正常(endpoint=/get, timeout=15s<EFBFBD><EFBFBD>?
|
||||
|
||||
**应验证:**
|
||||
- 请求 A 和 B 并发执行(不互相阻塞)
|
||||
- 请求 B 成功(不受 A 超时影响)
|
||||
- 请求 A <EFBFBD><EFBFBD>?B 并发执行(不互相阻塞<EFBFBD><EFBFBD>?
|
||||
- 请求 B 成功(不<EFBFBD><EFBFBD>?A 超时影响<EFBFBD><EFBFBD>?
|
||||
- 请求 A 的超时连接被正确移除
|
||||
- Pool 中只有请求 B 的连接
|
||||
- Pool 中只有请<EFBFBD><EFBFBD>?B 的连<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
### 6. 多端口场景下的超时隔离
|
||||
### 6. 多端口场景下的超时隔<EFBFBD><EFBFBD>?
|
||||
|
||||
**场景**:
|
||||
**场景**<EFBFBD><EFBFBD>?
|
||||
1. 端口 11443 请求超时
|
||||
2. 端口 11444 请求正常
|
||||
3. 验证端口间隔离
|
||||
3. 验证端口间隔<EFBFBD><EFBFBD>?
|
||||
|
||||
**应验证:**
|
||||
- 端口 11443 pool count = 0
|
||||
@@ -175,52 +175,52 @@ XCTAssertEqual(client.connectionReuseCount, 0, @"No reuse for timed-out connecti
|
||||
**P.3 并发部分超时**
|
||||
- `testTimeout_ConcurrentPartialTimeout_SuccessfulRequestsReuse`
|
||||
|
||||
**P.4 连续超时无泄漏**
|
||||
**P.4 连续超时无泄<EFBFBD><EFBFBD>?*
|
||||
- `testTimeout_ConsecutiveTimeouts_NoConnectionLeak`
|
||||
|
||||
**P.5 超时不阻塞池**
|
||||
- `testTimeout_NonBlocking_ConcurrentNormalRequestSucceeds`
|
||||
|
||||
**P.6 多端口超时隔离**
|
||||
**P.6 多端口超时隔<EFBFBD><EFBFBD>?*
|
||||
- `testTimeout_MultiPort_IsolatedPoolCleaning`
|
||||
|
||||
---
|
||||
|
||||
## Mock Server 支持
|
||||
|
||||
需要添加可配置延迟的 endpoint:
|
||||
- `/delay/10` - 延迟 10 秒(已有)
|
||||
- 测试时设置短 timeout(如 0.5s-2s)触发超时
|
||||
需要添加可配置延迟<EFBFBD><EFBFBD>?endpoint<EFBFBD><EFBFBD>?
|
||||
- `/delay/10` - 延迟 10 秒(已有<EFBFBD><EFBFBD>?
|
||||
- 测试时设置短 timeout(如 0.5s-2s)触发超<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
## 预期测试结果
|
||||
|
||||
| 验证项 | 当前状态 | 目标状态 |
|
||||
| 验证<EFBFBD><EFBFBD>?| 当前状<EFBFBD><EFBFBD>?| 目标状<EFBFBD><EFBFBD>?|
|
||||
|--------|---------|---------|
|
||||
| 超时连接移除 | 未验证 | ✅ 验证池计数=0 |
|
||||
| 池恢复能力 | 未验证 | ✅ 后续请求成功 |
|
||||
| 并发超时隔离 | 未验证 | ✅ 成功请求不受影响 |
|
||||
| 无连接泄漏 | 未验证 | ✅ 总连接数稳定 |
|
||||
| 超时不阻塞 | 未验证 | ✅ 并发执行不阻塞 |
|
||||
| 多端口隔离 | 未验证 | ✅ 端口间独立清理 |
|
||||
| 超时连接移除 | 未验<EFBFBD><EFBFBD>?| <20><>?验证池计<EFBFBD><EFBFBD>?0 |
|
||||
| 池恢复能<EFBFBD><EFBFBD>?| 未验<EFBFBD><EFBFBD>?| <20><>?后续请求成功 |
|
||||
| 并发超时隔离 | 未验<EFBFBD><EFBFBD>?| <20><>?成功请求不受影响 |
|
||||
| 无连接泄<EFBFBD><EFBFBD>?| 未验<EFBFBD><EFBFBD>?| <20><>?总连接数稳定 |
|
||||
| 超时不阻<EFBFBD><EFBFBD>?| 未验<EFBFBD><EFBFBD>?| <20><>?并发执行不阻<EFBFBD><EFBFBD>?|
|
||||
| 多端口隔<EFBFBD><EFBFBD>?| 未验<EFBFBD><EFBFBD>?| <20><>?端口间独立清<EFBFBD><EFBFBD>?|
|
||||
|
||||
---
|
||||
|
||||
## 风险评估
|
||||
|
||||
**如果不测试这些场景的风险:**
|
||||
1. **连接泄漏**:超时连接可能未正确清理,导致内存泄漏
|
||||
2. **池污染**:超时连接留在池中,被后续请求复用导致失败
|
||||
**如果不测试这些场景的风险<EFBFBD><EFBFBD>?*
|
||||
1. **连接泄漏**:超时连接可能未正确清理,导致内存泄<EFBFBD><EFBFBD>?
|
||||
2. **池污<EFBFBD><EFBFBD>?*:超时连接留在池中,被后续请求复用导致失<EFBFBD><EFBFBD>?
|
||||
3. **级联故障**:部分超时影响整体连接池健康
|
||||
4. **资源耗尽**:连续超时累积连接,最终耗尽系统资源
|
||||
|
||||
**当前代码逻辑正确性:** ✅ 高(代码分析显示正确处理)
|
||||
**测试验证覆盖率:** ❌ 低(缺少池交互验证)
|
||||
**当前代码逻辑正确性:** <EFBFBD><EFBFBD>?高(代码分析显示正确处理<EFBFBD><EFBFBD>?
|
||||
**测试验证覆盖率:** <EFBFBD><EFBFBD>?低(缺少池交互验证)
|
||||
|
||||
**建议:** 添加 P 组测试以提供**可观测的证据**证明超时处理正确。
|
||||
**建议<EFBFBD><EFBFBD>?* 添加 P 组测试以提供**可观测的证据**证明超时处理正确<EFBFBD><EFBFBD>?
|
||||
|
||||
---
|
||||
|
||||
**创建时间**: 2025-11-01
|
||||
**维护者**: Claude Code
|
||||
**维护<EFBFBD><EFBFBD>?*: Claude Code
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#import <SystemConfiguration/CaptiveNetwork.h>
|
||||
#import <UIKit/UIDevice.h>
|
||||
|
||||
static char *const networkManagerQueue = "com.alibaba.managerQueue";
|
||||
static char *const networkManagerQueue = "com.Trust.managerQueue";
|
||||
static dispatch_queue_t reachabilityQueue;
|
||||
|
||||
@implementation NetworkManager {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// TestBase.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/4/14.
|
||||
// Copyright © 2017年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// TestBase.m
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/4/14.
|
||||
// Copyright © 2017年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TestBase.h"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// TestBase.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/4/14.
|
||||
// Copyright © 2017年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// TestBase.h
|
||||
// AlicloudHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by ElonChan(地风) on 2017/4/14.
|
||||
// Copyright © 2017年 alibaba-inc.com. All rights reserved.
|
||||
// Copyright © 2017<EFBFBD><EFBFBD>?trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import "XCTestCase+AsyncTesting.h"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# HTTPDNS iOS SDK (SNI Hidden v1.0.0)
|
||||
# HTTPDNS iOS SDK (SNI Hidden v1.0.0)
|
||||
|
||||
## 1. Init
|
||||
|
||||
```objc
|
||||
#import <AlicloudHTTPDNS/AlicloudHTTPDNS.h>
|
||||
#import <TrustHTTPDNS/TrustHTTPDNS.h>
|
||||
|
||||
HttpdnsEdgeService *service = [[HttpdnsEdgeService alloc]
|
||||
initWithAppId:@"app1f1ndpo9"
|
||||
|
||||
Reference in New Issue
Block a user