管理端全部功能跑通
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "AlicloudHTTPDNS"
|
||||
s.version = "3.4.1"
|
||||
s.version = "1.0.0"
|
||||
s.summary = "Aliyun Mobile Service HTTPDNS iOS SDK (source distribution)."
|
||||
s.description = <<-DESC
|
||||
HTTPDNS iOS SDK 源码分发版本,提供通过 HTTP(S) 进行域名解析、
|
||||
@@ -27,6 +27,7 @@ Pod::Spec.new do |s|
|
||||
s.public_header_files = [
|
||||
"AlicloudHttpDNS/AlicloudHttpDNS.h",
|
||||
"AlicloudHttpDNS/HttpdnsService.h",
|
||||
"AlicloudHttpDNS/HttpdnsEdgeService.h",
|
||||
"AlicloudHttpDNS/Model/HttpdnsResult.h",
|
||||
"AlicloudHttpDNS/Model/HttpdnsRequest.h",
|
||||
"AlicloudHttpDNS/Log/HttpdnsLog.h",
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#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>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef PublicConstant_h
|
||||
#define PublicConstant_h
|
||||
|
||||
static NSString *const HTTPDNS_IOS_SDK_VERSION = @"3.4.1";
|
||||
static NSString *const HTTPDNS_IOS_SDK_VERSION = @"1.0.0";
|
||||
|
||||
#define ALICLOUD_HTTPDNS_DEFAULT_REGION_KEY @"cn"
|
||||
#define ALICLOUD_HTTPDNS_HONGKONG_REGION_KEY @"hk"
|
||||
|
||||
36
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/HttpdnsEdgeService.h
Normal file
36
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/HttpdnsEdgeService.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface HttpdnsEdgeResolveResult : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSString *requestId;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *ipv4s;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *ipv6s;
|
||||
@property (nonatomic, assign) NSInteger ttl;
|
||||
|
||||
@end
|
||||
|
||||
@interface HttpdnsEdgeService : NSObject
|
||||
|
||||
- (instancetype)initWithAppId:(NSString *)appId
|
||||
primaryServiceHost:(NSString *)primaryServiceHost
|
||||
backupServiceHost:(nullable NSString *)backupServiceHost
|
||||
servicePort:(NSInteger)servicePort
|
||||
signSecret:(nullable NSString *)signSecret;
|
||||
|
||||
- (void)resolveHost:(NSString *)host
|
||||
queryType:(NSString *)queryType
|
||||
completion:(void (^)(HttpdnsEdgeResolveResult *_Nullable result, NSError *_Nullable error))completion;
|
||||
|
||||
/// Connect by IP + HTTPS and keep Host header as business domain.
|
||||
/// This path will not fallback to domain connect.
|
||||
- (void)requestURL:(NSURL *)url
|
||||
method:(NSString *)method
|
||||
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
|
||||
body:(nullable NSData *)body
|
||||
completion:(void (^)(NSData *_Nullable data, NSHTTPURLResponse *_Nullable response, NSError *_Nullable error))completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
277
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/HttpdnsEdgeService.m
Normal file
277
EdgeHttpDNS/sdk/ios/AlicloudHttpDNS/HttpdnsEdgeService.m
Normal file
@@ -0,0 +1,277 @@
|
||||
#import "HttpdnsEdgeService.h"
|
||||
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
|
||||
static NSString * const kHttpdnsEdgeErrorDomain = @"com.goeedge.httpdns.edge";
|
||||
|
||||
@implementation HttpdnsEdgeResolveResult
|
||||
@end
|
||||
|
||||
@interface HttpdnsEdgeService ()
|
||||
|
||||
@property (nonatomic, copy) NSString *appId;
|
||||
@property (nonatomic, copy) NSString *primaryServiceHost;
|
||||
@property (nonatomic, copy) NSString *backupServiceHost;
|
||||
@property (nonatomic, assign) NSInteger servicePort;
|
||||
@property (nonatomic, copy) NSString *signSecret;
|
||||
@property (nonatomic, copy) NSString *sessionId;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HttpdnsEdgeService
|
||||
|
||||
- (instancetype)initWithAppId:(NSString *)appId
|
||||
primaryServiceHost:(NSString *)primaryServiceHost
|
||||
backupServiceHost:(NSString *)backupServiceHost
|
||||
servicePort:(NSInteger)servicePort
|
||||
signSecret:(NSString *)signSecret {
|
||||
if (self = [super init]) {
|
||||
_appId = [appId copy];
|
||||
_primaryServiceHost = [primaryServiceHost copy];
|
||||
_backupServiceHost = backupServiceHost.length > 0 ? [backupServiceHost copy] : @"";
|
||||
_servicePort = servicePort > 0 ? servicePort : 443;
|
||||
_signSecret = signSecret.length > 0 ? [signSecret copy] : @"";
|
||||
_sessionId = [[[NSUUID UUID].UUIDString stringByReplacingOccurrencesOfString:@"-" withString:@""] copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)resolveHost:(NSString *)host
|
||||
queryType:(NSString *)queryType
|
||||
completion:(void (^)(HttpdnsEdgeResolveResult *_Nullable, NSError *_Nullable))completion {
|
||||
if (host.length == 0 || self.appId.length == 0 || self.primaryServiceHost.length == 0) {
|
||||
NSError *error = [NSError errorWithDomain:kHttpdnsEdgeErrorDomain
|
||||
code:1001
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"invalid init config or host"}];
|
||||
completion(nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *qtype = queryType.length > 0 ? queryType.uppercaseString : @"A";
|
||||
NSArray<NSString *> *hosts = self.backupServiceHost.length > 0
|
||||
? @[self.primaryServiceHost, self.backupServiceHost]
|
||||
: @[self.primaryServiceHost];
|
||||
|
||||
[self requestResolveHosts:hosts index:0 host:host qtype:qtype completion:completion];
|
||||
}
|
||||
|
||||
- (void)requestURL:(NSURL *)url
|
||||
method:(NSString *)method
|
||||
headers:(NSDictionary<NSString *,NSString *> *)headers
|
||||
body:(NSData *)body
|
||||
completion:(void (^)(NSData *_Nullable, NSHTTPURLResponse *_Nullable, NSError *_Nullable))completion {
|
||||
NSString *originHost = url.host ?: @"";
|
||||
if (originHost.length == 0) {
|
||||
NSError *error = [NSError errorWithDomain:kHttpdnsEdgeErrorDomain
|
||||
code:2001
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"invalid request host"}];
|
||||
completion(nil, nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
[self resolveHost:originHost queryType:@"A" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
if (error != nil || result.ipv4s.count == 0) {
|
||||
completion(nil, nil, error ?: [NSError errorWithDomain:kHttpdnsEdgeErrorDomain
|
||||
code:2002
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"NO_IP_AVAILABLE"}]);
|
||||
return;
|
||||
}
|
||||
|
||||
[self requestByIPList:result.ipv4s
|
||||
index:0
|
||||
url:url
|
||||
method:method
|
||||
headers:headers
|
||||
body:body
|
||||
completion:completion];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)requestByIPList:(NSArray<NSString *> *)ips
|
||||
index:(NSInteger)index
|
||||
url:(NSURL *)url
|
||||
method:(NSString *)method
|
||||
headers:(NSDictionary<NSString *, NSString *> *)headers
|
||||
body:(NSData *)body
|
||||
completion:(void (^)(NSData *_Nullable, NSHTTPURLResponse *_Nullable, NSError *_Nullable))completion {
|
||||
if (index >= ips.count) {
|
||||
NSError *error = [NSError errorWithDomain:kHttpdnsEdgeErrorDomain
|
||||
code:2003
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"TLS_EMPTY_SNI_FAILED"}];
|
||||
completion(nil, nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *ip = ips[index];
|
||||
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
|
||||
components.host = ip;
|
||||
if (components.port == nil) {
|
||||
components.port = @(443);
|
||||
}
|
||||
|
||||
NSURL *targetURL = components.URL;
|
||||
if (targetURL == nil) {
|
||||
[self requestByIPList:ips index:index + 1 url:url method:method headers:headers body:body completion:completion];
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:targetURL
|
||||
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval:8];
|
||||
request.HTTPMethod = method.length > 0 ? method : @"GET";
|
||||
if (body.length > 0) {
|
||||
request.HTTPBody = body;
|
||||
}
|
||||
|
||||
[request setValue:url.host forHTTPHeaderField:@"Host"];
|
||||
[headers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) {
|
||||
if ([key.lowercaseString isEqualToString:@"host"]) {
|
||||
return;
|
||||
}
|
||||
[request setValue:obj forHTTPHeaderField:key];
|
||||
}];
|
||||
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
config.timeoutIntervalForRequest = 8;
|
||||
config.timeoutIntervalForResource = 8;
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
|
||||
|
||||
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
[session finishTasksAndInvalidate];
|
||||
|
||||
if (error != nil) {
|
||||
[self requestByIPList:ips index:index + 1 url:url method:method headers:headers body:body completion:completion];
|
||||
return;
|
||||
}
|
||||
|
||||
completion(data, (NSHTTPURLResponse *)response, nil);
|
||||
}] resume];
|
||||
}
|
||||
|
||||
- (void)requestResolveHosts:(NSArray<NSString *> *)hosts
|
||||
index:(NSInteger)index
|
||||
host:(NSString *)host
|
||||
qtype:(NSString *)qtype
|
||||
completion:(void (^)(HttpdnsEdgeResolveResult *_Nullable result, NSError *_Nullable error))completion {
|
||||
if (index >= hosts.count) {
|
||||
NSError *error = [NSError errorWithDomain:kHttpdnsEdgeErrorDomain
|
||||
code:1002
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"resolve failed on all service hosts"}];
|
||||
completion(nil, error);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *serviceHost = hosts[index];
|
||||
NSURLComponents *components = [NSURLComponents new];
|
||||
components.scheme = @"https";
|
||||
components.host = serviceHost;
|
||||
components.port = @(self.servicePort);
|
||||
components.path = @"/resolve";
|
||||
|
||||
NSMutableArray<NSURLQueryItem *> *items = [NSMutableArray arrayWithArray:@[
|
||||
[NSURLQueryItem queryItemWithName:@"appId" value:self.appId],
|
||||
[NSURLQueryItem queryItemWithName:@"dn" value:host],
|
||||
[NSURLQueryItem queryItemWithName:@"qtype" value:qtype],
|
||||
[NSURLQueryItem queryItemWithName:@"sid" value:self.sessionId],
|
||||
[NSURLQueryItem queryItemWithName:@"sdk_version" value:@"ios-native-1.0.0"],
|
||||
[NSURLQueryItem queryItemWithName:@"os" value:@"ios"],
|
||||
]];
|
||||
|
||||
if (self.signSecret.length > 0) {
|
||||
NSString *exp = [NSString stringWithFormat:@"%ld", (long)([[NSDate date] timeIntervalSince1970] + 600)];
|
||||
NSString *nonce = [[NSUUID UUID].UUIDString stringByReplacingOccurrencesOfString:@"-" withString:@""];
|
||||
NSString *raw = [NSString stringWithFormat:@"%@|%@|%@|%@|%@",
|
||||
self.appId,
|
||||
host.lowercaseString,
|
||||
qtype.uppercaseString,
|
||||
exp,
|
||||
nonce];
|
||||
NSString *sign = [self hmacSha256Hex:raw secret:self.signSecret];
|
||||
[items addObject:[NSURLQueryItem queryItemWithName:@"exp" value:exp]];
|
||||
[items addObject:[NSURLQueryItem queryItemWithName:@"nonce" value:nonce]];
|
||||
[items addObject:[NSURLQueryItem queryItemWithName:@"sign" value:sign]];
|
||||
}
|
||||
|
||||
components.queryItems = items;
|
||||
NSURL *url = components.URL;
|
||||
if (url == nil) {
|
||||
[self requestResolveHosts:hosts index:index + 1 host:host qtype:qtype completion:completion];
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
|
||||
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||
timeoutInterval:6];
|
||||
request.HTTPMethod = @"GET";
|
||||
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
config.timeoutIntervalForRequest = 6;
|
||||
config.timeoutIntervalForResource = 6;
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
|
||||
|
||||
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
[session finishTasksAndInvalidate];
|
||||
|
||||
if (error != nil || data.length == 0) {
|
||||
[self requestResolveHosts:hosts index:index + 1 host:host qtype:qtype completion:completion];
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
if (![json isKindOfClass:NSDictionary.class]) {
|
||||
[self requestResolveHosts:hosts index:index + 1 host:host qtype:qtype completion:completion];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *code = [NSString stringWithFormat:@"%@", json[@"code"] ?: @""];
|
||||
if (![code.uppercaseString isEqualToString:@"SUCCESS"]) {
|
||||
[self requestResolveHosts:hosts index:index + 1 host:host qtype:qtype completion:completion];
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *dataJSON = [json[@"data"] isKindOfClass:NSDictionary.class] ? json[@"data"] : @{};
|
||||
NSArray *records = [dataJSON[@"records"] isKindOfClass:NSArray.class] ? dataJSON[@"records"] : @[];
|
||||
|
||||
NSMutableArray<NSString *> *ipv4 = [NSMutableArray array];
|
||||
NSMutableArray<NSString *> *ipv6 = [NSMutableArray array];
|
||||
for (NSDictionary *row in records) {
|
||||
NSString *type = [NSString stringWithFormat:@"%@", row[@"type"] ?: @""];
|
||||
NSString *ip = [NSString stringWithFormat:@"%@", row[@"ip"] ?: @""];
|
||||
if (ip.length == 0) {
|
||||
continue;
|
||||
}
|
||||
if ([type.uppercaseString isEqualToString:@"AAAA"]) {
|
||||
[ipv6 addObject:ip];
|
||||
} else {
|
||||
[ipv4 addObject:ip];
|
||||
}
|
||||
}
|
||||
|
||||
HttpdnsEdgeResolveResult *result = [HttpdnsEdgeResolveResult new];
|
||||
result.requestId = [NSString stringWithFormat:@"%@", json[@"requestId"] ?: @""];
|
||||
result.ipv4s = ipv4;
|
||||
result.ipv6s = ipv6;
|
||||
result.ttl = [dataJSON[@"ttl"] integerValue];
|
||||
completion(result, nil);
|
||||
}] resume];
|
||||
}
|
||||
|
||||
- (NSString *)hmacSha256Hex:(NSString *)data secret:(NSString *)secret {
|
||||
NSData *keyData = [secret dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *messageData = [data dataUsingEncoding:NSUTF8StringEncoding];
|
||||
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
|
||||
CCHmac(kCCHmacAlgSHA256,
|
||||
keyData.bytes,
|
||||
keyData.length,
|
||||
messageData.bytes,
|
||||
messageData.length,
|
||||
cHMAC);
|
||||
|
||||
NSMutableString *result = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
|
||||
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
|
||||
[result appendFormat:@"%02x", cHMAC[i]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,73 +1,58 @@
|
||||
# Alicloud HTTPDNS iOS SDK
|
||||
# HTTPDNS iOS SDK (SNI Hidden v1.0.0)
|
||||
|
||||
面向 iOS 的 HTTP/HTTPS DNS 解析 SDK,提供鉴权与可选 AES 加密、IPv4/IPv6 双栈解析、缓存与调度、预解析等能力。最低支持 iOS 12.0。
|
||||
## 1. Init
|
||||
|
||||
## 功能特性
|
||||
- 鉴权请求与可选 AES 传输加密
|
||||
- IPv4/IPv6 双栈解析,支持自动/同时解析
|
||||
- 内存 + 持久化缓存与 TTL 控制,可选择复用过期 IP
|
||||
- 预解析、区域路由、网络切换自动刷新
|
||||
- 可定制日志回调与会话追踪 `sessionId`
|
||||
|
||||
## 安装(CocoaPods)
|
||||
在 `Podfile` 中添加:
|
||||
|
||||
```ruby
|
||||
platform :ios, '12.0'
|
||||
target 'YourApp' do
|
||||
pod 'AlicloudHTTPDNS', '~> 3.4.1'
|
||||
end
|
||||
```
|
||||
|
||||
执行 `pod install` 安装依赖。
|
||||
|
||||
## 快速开始
|
||||
Objective‑C
|
||||
```objc
|
||||
#import <AlicloudHttpDNS/AlicloudHttpDNS.h>
|
||||
#import <AlicloudHTTPDNS/AlicloudHTTPDNS.h>
|
||||
|
||||
// 使用鉴权初始化(单例);密钥请勿硬编码到仓库
|
||||
HttpDnsService *service = [[HttpDnsService alloc] initWithAccountID:1000000
|
||||
secretKey:@"<YOUR_SECRET>"];
|
||||
[service setPersistentCacheIPEnabled:YES];
|
||||
[service setPreResolveHosts:@[@"www.aliyun.com"] byIPType:HttpdnsQueryIPTypeAuto];
|
||||
|
||||
// 同步解析(会根据网络自动选择 v4/v6)
|
||||
HttpdnsResult *r = [service resolveHostSync:@"www.aliyun.com" byIpType:HttpdnsQueryIPTypeAuto];
|
||||
NSLog(@"IPv4: %@", r.ips);
|
||||
HttpdnsEdgeService *service = [[HttpdnsEdgeService alloc]
|
||||
initWithAppId:@"app1f1ndpo9"
|
||||
primaryServiceHost:@"httpdns-a.example.com"
|
||||
backupServiceHost:@"httpdns-b.example.com"
|
||||
servicePort:443
|
||||
signSecret:@"your-sign-secret"]; // optional if sign is enabled
|
||||
```
|
||||
|
||||
Swift
|
||||
```swift
|
||||
import AlicloudHttpDNS
|
||||
## 2. Resolve
|
||||
|
||||
_ = HttpDnsService(accountID: 1000000, secretKey: "<YOUR_SECRET>")
|
||||
let svc = HttpDnsService.sharedInstance()
|
||||
let res = svc?.resolveHostSync("www.aliyun.com", byIpType: .auto)
|
||||
print(res?.ips ?? [])
|
||||
```objc
|
||||
[service resolveHost:@"api.business.com" queryType:@"A" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
if (error != nil) {
|
||||
return;
|
||||
}
|
||||
NSLog(@"requestId=%@ ipv4=%@ ipv6=%@ ttl=%ld", result.requestId, result.ipv4s, result.ipv6s, (long)result.ttl);
|
||||
}];
|
||||
```
|
||||
|
||||
提示
|
||||
- 启动时通过 `setPreResolveHosts(_:byIPType:)` 预热热点域名。
|
||||
- 如需在刷新期间容忍 TTL 过期,可开启 `setReuseExpiredIPEnabled:YES`。
|
||||
- 使用 `getSessionId()` 并与选用 IP 一同记录,便于排障。
|
||||
## 3. Official Request Adapter (IP + Host)
|
||||
|
||||
## 源码构建
|
||||
执行 `./build_xc_framework.sh` 生成 XCFramework。脚本会从 `gitlab.alibaba-inc.com` 克隆内部构建工具;外部环境建议优先使用 CocoaPods 引入。
|
||||
```objc
|
||||
NSURL *url = [NSURL URLWithString:@"https://api.business.com/v1/ping"];
|
||||
[service requestURL:url method:@"GET" headers:@{@"Accept": @"application/json"} body:nil completion:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
if (error != nil) {
|
||||
return;
|
||||
}
|
||||
NSLog(@"status=%ld", (long)response.statusCode);
|
||||
}];
|
||||
```
|
||||
|
||||
## 测试
|
||||
- 在 Xcode 使用 Scheme `AlicloudHttpDNSTests` 运行。
|
||||
- 含 OCMock 的用例批量执行可能出现内存问题,请单个运行。
|
||||
- 非 Mock 用例使用预置参数:AccountID `1000000`,测试域名 `*.onlyforhttpdnstest.run.place`(每年需续期)。
|
||||
Behavior is fixed:
|
||||
- Resolve by `/resolve`.
|
||||
- Connect to resolved IP over HTTPS.
|
||||
- Keep `Host` header as business domain.
|
||||
- No fallback to domain direct request.
|
||||
|
||||
## 依赖与链接
|
||||
- iOS 12.0+;需链接 `CoreTelephony`、`SystemConfiguration`
|
||||
- 额外库:`sqlite3.0`、`resolv`;`OTHER_LDFLAGS` 包含 `-ObjC -lz`
|
||||
## 4. Public Errors
|
||||
|
||||
## 安全说明
|
||||
- 切勿提交真实的 AccountID/SecretKey,請通过本地安全配置或 CI 注入。
|
||||
- 若担心设备时间偏差影响鉴权,可用 `setInternalAuthTimeBaseBySpecifyingCurrentTime:` 校正。
|
||||
- `NO_IP_AVAILABLE`
|
||||
- `TLS_EMPTY_SNI_FAILED`
|
||||
- `HOST_ROUTE_REJECTED`
|
||||
- `RESOLVE_SIGN_INVALID`
|
||||
|
||||
## Demo 与贡献
|
||||
- 示例应用:`AlicloudHttpDNSTestDemo/`
|
||||
- 贡献与提交流程请参见 `AGENTS.md`(提交信息与 PR 规范)。
|
||||
## 5. Removed Public Params
|
||||
|
||||
Do not expose legacy public parameters:
|
||||
- `accountId`
|
||||
- `serviceDomain`
|
||||
- `endpoint`
|
||||
- `aesSecretKey`
|
||||
|
||||
Reference in New Issue
Block a user