sdk final
This commit is contained in:
@@ -1,133 +0,0 @@
|
||||
# Flutter SDK 集成文档(Edge HTTPDNS)
|
||||
|
||||
## 1. 版本与依赖
|
||||
|
||||
- SDK 插件:`HttpDNSSDK/sdk/flutter/new_httpdns`
|
||||
- 环境要求:Flutter 2.15+ / Dart 2.15+
|
||||
|
||||
在 `pubspec.yaml` 中引用本地插件:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
new_httpdns:
|
||||
path: path/to/sdk/flutter/new_httpdns
|
||||
```
|
||||
|
||||
执行 `flutter pub get` 完成安装。
|
||||
|
||||
## 2. SNI 行为说明(关键)
|
||||
|
||||
1. **/resolve 请求链路**(SDK -> 你的 HTTPDNS 服务域名)
|
||||
- 走标准 HTTPS,默认携带 SNI(用于路由到边缘控制节点)。
|
||||
|
||||
2. **业务请求链路**(拿到 CDN IP 后发起业务 HTTPS)
|
||||
- **IP 直连 + No-SNI**:使用 `TrustAPPHttpdnsHttpAdapter` 进行请求。
|
||||
- 逻辑:解析域名 -> 拿到 IP 列表 -> `uri.replace(host: ip)` -> `req.headers.host = uri.host` -> **清空 SNI**。
|
||||
- 仅支持 HTTPS URL。
|
||||
|
||||
## 3. 初始化 SDK(推荐用 TrustAPP 封装)
|
||||
|
||||
### Dart
|
||||
|
||||
```dart
|
||||
import 'package:new_httpdns/new_httpdns.dart';
|
||||
|
||||
bool ok = await TrustAPPHttpdns.init(
|
||||
appId: "your-app-id",
|
||||
primaryServiceHost: "httpdns.example.com",
|
||||
backupServiceHost: "httpdns-backup.example.com",
|
||||
servicePort: 443,
|
||||
secretKey: "your-sign-secret" // 可选,开启签名校验需传入
|
||||
);
|
||||
|
||||
if (ok) {
|
||||
print("Edge HTTPDNS 初始化成功");
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 解析域名获取 CDN IP
|
||||
|
||||
### Dart
|
||||
|
||||
```dart
|
||||
// V1 风格解析接口
|
||||
Map<String, dynamic> result = await TrustAPPHttpdns.resolveHost(
|
||||
"api.example.com",
|
||||
qtype: 'A', // 可选 'A' 或 'AAAA'
|
||||
cip: '1.2.3.4' // 可选,模拟客户端 IP
|
||||
);
|
||||
|
||||
List<String> ipv4s = result['ipv4'];
|
||||
int ttl = result['ttl'];
|
||||
```
|
||||
|
||||
## 5. 业务请求接入方式
|
||||
|
||||
使用 `TrustAPPHttpdnsHttpAdapter` 实现“SNI 隐匿”业务请求。
|
||||
|
||||
### Dart
|
||||
|
||||
```dart
|
||||
final adapter = TrustAPPHttpdns.createHttpAdapter(
|
||||
options: const TrustAPPHttpdnsAdapterOptions(
|
||||
connectTimeoutMs: 3000,
|
||||
readTimeoutMs: 5000,
|
||||
ipType: 'auto', // auto/ipv4/ipv6
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
final res = await adapter.request(
|
||||
Uri.parse("https://api.example.com/path?x=1"),
|
||||
method: 'GET',
|
||||
headers: {'Custom-Header': 'Value'},
|
||||
body: null
|
||||
);
|
||||
|
||||
print("Status Code: ${res.statusCode}");
|
||||
print("Body Length: ${res.body.length}");
|
||||
print("Used IP: ${res.usedIp}");
|
||||
} catch (e) {
|
||||
print("请求失败: $e");
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 其他常用接口
|
||||
|
||||
```dart
|
||||
// 1. 设置预解析域名
|
||||
await TrustAPPHttpdns.setPreResolveHosts(["api.example.com", "img.example.com"]);
|
||||
|
||||
// 2. 只有开启缓存时可用
|
||||
Map<String, List<String>> cacheRes = await TrustAPPHttpdns.resolveHostSyncNonBlocking("api.example.com");
|
||||
|
||||
// 3. 开启持久化缓存(重启 App 后任然可用)
|
||||
await TrustAPPHttpdns.setPersistentCacheIPEnabled(true);
|
||||
|
||||
// 4. 控制台日志(建议仅调试开启)
|
||||
await TrustAPPHttpdns.setLogEnabled(true);
|
||||
```
|
||||
|
||||
## 7. 验证建议
|
||||
|
||||
1. **验证 /resolve**
|
||||
- 观察控制台日志或抓包工具,解析请求应指向 `https://<serviceHost>:<servicePort>/resolve...`。
|
||||
|
||||
2. **验证业务请求**
|
||||
- 如果使用 `TrustAPPHttpdnsHttpAdapter`,观察抓包:
|
||||
- TCP 连接 IP 为 CDN 私有/边缘 IP。
|
||||
- HTTP `Host` 为原始域名。
|
||||
- TLS ClientHello 中 **无 SNI** 扩展。
|
||||
|
||||
## 8. 平台配置事项
|
||||
|
||||
- **Android**: 参照 Android SDK 文档配置混淆。
|
||||
- **iOS**: 如果是手动集成 Flutter 插件,请确保 iOS 模块已包含依赖的静态库,并设置 `Allow Arbitrary Loads` (如果启用 HTTP)。
|
||||
|
||||
## 9. 常见问题
|
||||
|
||||
1. **Flutter 端报错:NO_IP_AVAILABLE**
|
||||
- SDK 尚未解析出有效结果,请确认域名是否已在控制台添加并配置规则。
|
||||
|
||||
2. **请求报错:TLS_EMPTY_SNI_FAILED**
|
||||
- 仅支持 HTTPS 网站。如果所有 IP 尝试均失败,请检查网络权限及服务端防火墙。
|
||||
@@ -1,161 +1,114 @@
|
||||
# HTTPDNS SDK 集成文档(Android)
|
||||
|
||||
## 1. 版本与依赖
|
||||
## 一、 准备工作
|
||||
1. **获取参数**:从控制台获取您的 `AppId`、`apiUrl` 和 `secretKey`(可选)。
|
||||
2. **系统要求**:Android 5.0 (API Level 21) 及以上。
|
||||
3. **网络要求**:应用需要具备 `INTERNET` 与 `ACCESS_NETWORK_STATE` 权限。
|
||||
|
||||
- SDK 模块:`HttpDNSSDK/sdk/android/httpdns-sdk`
|
||||
- `minSdkVersion`:19
|
||||
- `targetSdkVersion`:33
|
||||
- `compileSdk`:33
|
||||
---
|
||||
|
||||
将发布包中的 `jar/aar` 放到应用模块 `libs/`,在 `app/build.gradle` 中添加:
|
||||
## 二、 安装配置
|
||||
|
||||
### 1. 导入 AAR/Jar
|
||||
将 SDK 发行包中的 `.aar` 文件复制到您 Android 工程的 `libs/` 目录下。
|
||||
|
||||
### 2. 配置 build.gradle
|
||||
在 App 模块的 `build.gradle` 中添加依赖引用:
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
// SDK 内部可能需要的系统库支持
|
||||
implementation 'androidx.annotation:annotation:1.5.0'
|
||||
}
|
||||
```
|
||||
|
||||
## 2. SNI 行为说明(关键)
|
||||
|
||||
当前 SDK 行为与代码一致:
|
||||
|
||||
1. `/resolve` 请求链路(SDK -> 你的 HTTPDNS 服务域名)
|
||||
- 走域名 HTTPS
|
||||
- 默认 TLS 行为(会带 SNI)
|
||||
|
||||
2. 业务请求链路(拿到 CDN IP 后发起业务 HTTPS)
|
||||
- 使用 `HttpDnsHttpAdapter`:按 IP 建连,`Host` 保留原域名,并清空 SNI(No-SNI)
|
||||
|
||||
## 3. 初始化 SDK(推荐用 V1 客户端)
|
||||
|
||||
### Kotlin
|
||||
|
||||
```kotlin
|
||||
import com.newsdk.sdk.android.httpdns.HttpDnsV1Client
|
||||
import com.newsdk.sdk.android.httpdns.HttpDnsService
|
||||
|
||||
val service: HttpDnsService = HttpDnsV1Client.init(
|
||||
applicationContext,
|
||||
"your-app-id",
|
||||
"https://httpdns.example.com:8445", // serviceUrl,支持填写协议+端口
|
||||
"your-sign-secret" // 可传 ""
|
||||
)
|
||||
### 3. 配置权限 (AndroidManifest.xml)
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
```
|
||||
|
||||
### Java
|
||||
---
|
||||
|
||||
## 三、 快速入门
|
||||
|
||||
### 1. 初始化 SDK
|
||||
推荐在自定义 `Application` 的 `onCreate` 中初始化,确保全局可用。
|
||||
|
||||
```java
|
||||
import com.newsdk.sdk.android.httpdns.HttpDnsService;
|
||||
import com.newsdk.sdk.android.httpdns.HttpDnsV1Client;
|
||||
// 建立配置
|
||||
InitConfig config = new InitConfig.Builder()
|
||||
.setContext(context)
|
||||
.setServiceUrl("https://httpdns.example.com:8445") // 统一配置协议、Host与端口
|
||||
.setSecretKey("your_secret_key") // 如果开启了签名校验
|
||||
.setEnableCacheIp(true) // 开启持久化缓存
|
||||
.setEnableExpiredIp(true) // 允许使用过期 IP(秒开优化)
|
||||
.buildFor("your_app_id");
|
||||
|
||||
HttpDnsService service = HttpDnsV1Client.init(
|
||||
getApplicationContext(),
|
||||
"your-app-id",
|
||||
"https://httpdns.example.com:8445", // serviceUrl
|
||||
"your-sign-secret" // 可传 ""
|
||||
);
|
||||
// 获取服务实例
|
||||
HttpDnsService service = HttpDns.getService(context, "your_app_id");
|
||||
```
|
||||
|
||||
## 4. 解析域名获取 CDN IP
|
||||
|
||||
### Kotlin
|
||||
|
||||
```kotlin
|
||||
import com.newsdk.sdk.android.httpdns.HTTPDNSResult
|
||||
|
||||
val result: HTTPDNSResult = HttpDnsV1Client.resolveHost(
|
||||
service = service,
|
||||
host = "api.example.com",
|
||||
qtype = "A", // "A" 或 "AAAA"
|
||||
cip = null // 可选,客户端 IP 透传
|
||||
)
|
||||
|
||||
val ips = result.ips ?: emptyArray()
|
||||
```
|
||||
|
||||
### Java
|
||||
### 2. 域名解析
|
||||
使用异步非阻塞接口获取 IP。
|
||||
|
||||
```java
|
||||
import com.newsdk.sdk.android.httpdns.HTTPDNSResult;
|
||||
|
||||
HTTPDNSResult result = HttpDnsV1Client.resolveHost(
|
||||
service,
|
||||
"api.example.com",
|
||||
"A",
|
||||
null
|
||||
);
|
||||
|
||||
String[] ips = result.getIps();
|
||||
service.getHttpDnsResultForHostSyncNonBlocking("www.example.com", RequestIpType.v4);
|
||||
// 或者使用更详细的异步回调
|
||||
```
|
||||
|
||||
## 5. 业务请求接入方式
|
||||
---
|
||||
|
||||
### 使用 `HttpDnsHttpAdapter`(IP 直连 + No-SNI)
|
||||
## 四、 企业级业务接入
|
||||
|
||||
业务请求侧做“隐匿 SNI”。该适配器仅支持 HTTPS URL。
|
||||
SDK 并不强制要求修改您的请求框架(如 OkHttp),但强烈建议在拦截器中集成。
|
||||
|
||||
```kotlin
|
||||
import com.newsdk.sdk.android.httpdns.HttpDnsV1Client
|
||||
import com.newsdk.sdk.android.httpdns.network.HttpDnsAdapterOptions
|
||||
import com.newsdk.sdk.android.httpdns.network.HttpDnsAdapterRequest
|
||||
### 1. IP 直连原理
|
||||
1. 通过 `getHttpDnsResultForHostSyncNonBlocking` 获取 IP。
|
||||
2. 将请求 URL 中的 Host 替换为 IP。
|
||||
3. 设置 HTTP 请求头中的 `Host` 字段为原始域名。
|
||||
|
||||
val adapter = HttpDnsV1Client.buildHttpClientAdapter(
|
||||
service,
|
||||
HttpDnsAdapterOptions.Builder()
|
||||
.setConnectTimeoutMillis(3000)
|
||||
.setReadTimeoutMillis(5000)
|
||||
.setRequestIpType(com.newsdk.sdk.android.httpdns.RequestIpType.auto)
|
||||
.build()
|
||||
)
|
||||
### 2. HTTPS 证书校验(针对 SNI)
|
||||
当使用 IP 直连访问 HTTPS 时,需要自定义 `HostnameVerifier`。
|
||||
|
||||
val req = HttpDnsAdapterRequest(
|
||||
"GET",
|
||||
"https://api.example.com/path?x=1"
|
||||
)
|
||||
|
||||
val resp = adapter.execute(req)
|
||||
val code = resp.statusCode
|
||||
val bodyBytes = resp.body
|
||||
val usedIp = resp.usedIp
|
||||
```java
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.hostnameVerifier((hostname, session) -> {
|
||||
// 自定义校验逻辑:验证当前请求的 IP 证书是否匹配原始业务域名
|
||||
return OkHostnameVerifier.INSTANCE.verify("api.example.com", session);
|
||||
})
|
||||
.build();
|
||||
```
|
||||
|
||||
## 6. 预解析与常用接口
|
||||
---
|
||||
|
||||
```kotlin
|
||||
service.setPreResolveHosts(listOf("api.example.com", "img.example.com"))
|
||||
## 五、 API 参考手册
|
||||
|
||||
val r1 = service.getHttpDnsResultForHostSync("api.example.com", com.newsdk.sdk.android.httpdns.RequestIpType.auto)
|
||||
val r2 = service.getHttpDnsResultForHostSyncNonBlocking("api.example.com", com.newsdk.sdk.android.httpdns.RequestIpType.auto)
|
||||
```
|
||||
| 方法名 | 说明 |
|
||||
| :--- | :--- |
|
||||
| `setServiceUrl(String)` | **核心**。通过 URL 一站式配置服务地址,自动处理 Scheme 和 Port。 |
|
||||
| `setEnableCacheIp(boolean)` | 启用持久化缓存,进程重启后能立即从磁盘读取上一次的解析结果。 |
|
||||
| `setPreResolveHosts(List)` | 预解析域名列表。建议将首屏需要的关键域名在此配置,大幅度提升首屏加载速度。 |
|
||||
| `setLogEnabled(boolean)` | 全局 Log 控制,开启后可通过 Logcat 观察 SDK 的详细调度逻辑。 |
|
||||
|
||||
- `Sync`:允许阻塞等待刷新结果(上限受 timeout 等配置影响)
|
||||
- `NonBlocking`:快速返回当前可用缓存/结果,不阻塞等待
|
||||
---
|
||||
|
||||
## 7. 验证建议
|
||||
## 六、 核心原理
|
||||
|
||||
1. 验证 `/resolve`
|
||||
- 抓包看目标应为 `https://<serviceUrl>/resolve...`(即初始化时传入的 URL)
|
||||
### 为什么我们更安全?
|
||||
Android 原生 DNS 解析极其依赖 `InetAddress`,而此方法通过系统的 `getaddrinfo` 实现,极其容易被运营商拦截或重定向。
|
||||
我们的 SDK 采用私有 HTTP 协议层,并配合 **HMAC-SHA256 签名机制**(如果配置了 secretKey),确保从域名解析到业务请求的每一环都是经过身份校验且不可篡改的。
|
||||
|
||||
2. 验证业务请求(若使用 `HttpDnsHttpAdapter`)
|
||||
- 目标地址应是 CDN IP
|
||||
- HTTP `Host` 应为原域名
|
||||
- TLS ClientHello 不应携带 SNI(No-SNI)
|
||||
---
|
||||
|
||||
## 8. 混淆配置
|
||||
## 七、 常见问题 (FAQ)
|
||||
|
||||
```proguard
|
||||
-keep class com.newsdk.sdk.android.** { *; }
|
||||
```
|
||||
**Q: 如何处理 IPv6 环境?**
|
||||
A: 初始化时无需特殊设置。请求解析时传入 `RequestIpType.both`,SDK 会尝试同时解析 A 和 AAAA 记录,并根据当前网络环境返回最优结果。
|
||||
|
||||
## 9. 常见问题
|
||||
**Q: AAR 编译报错:Duplicate Class?**
|
||||
A: 请确保 libs 下没有重复版本的 SDK,并检查是否重复引入了某些兼容包。
|
||||
|
||||
1. HTTPDNS 没生效
|
||||
- 检查是否真正使用了 SDK 返回 IP(或用了 `HttpDnsHttpAdapter`)
|
||||
- 检查失败回退逻辑是否总是直接走了系统 DNS
|
||||
|
||||
2. 使用 `HttpDnsHttpAdapter` 仍失败
|
||||
- 只支持 HTTPS URL
|
||||
|
||||
3. 线上不要开启不安全证书
|
||||
- `HttpDnsAdapterOptions.Builder#setAllowInsecureCertificatesForDebugOnly(true)` 仅限调试环境
|
||||
**Q: 日志里看到 "HTTPDNS resolve failed",该如何排查?**
|
||||
A: 1. 检查 `apiUrl` 是否可直接通过浏览器/Curl 访问。 2. 检查 `AppId` 是否正确。 3. 检查手机系统时间是否正确(如果开启了签名防伪)。
|
||||
|
||||
119
HttpDNSSDK/docs/HTTPDNS SDK 集成文档(Flutter).md
Normal file
119
HttpDNSSDK/docs/HTTPDNS SDK 集成文档(Flutter).md
Normal file
@@ -0,0 +1,119 @@
|
||||
# HTTPDNS SDK 集成文档(Flutter)
|
||||
|
||||
## 一、 准备工作
|
||||
1. **获取参数**:从控制台获取您的 `AppId` 和 `apiUrl`。
|
||||
2. **环境要求**:Flutter 2.15+ / Dart 2.15+。
|
||||
3. **原生环境**:
|
||||
- iOS 12.0+
|
||||
- Android API Level 21+
|
||||
|
||||
---
|
||||
|
||||
## 二、 安装配置
|
||||
|
||||
### 1. 引用本地插件
|
||||
将插件源码 `new_httpdns` 放置在您的项目目录中(例如 `packages/` 目录下),然后在 `pubspec.yaml` 中引用:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
new_httpdns:
|
||||
path: ./packages/new_httpdns
|
||||
```
|
||||
|
||||
### 2. 执行更新
|
||||
在终端运行:
|
||||
```bash
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、 快速入门
|
||||
|
||||
### 1. 初始化 SDK
|
||||
在 `main()` 函数中完成初始化工作。
|
||||
|
||||
```dart
|
||||
import 'package:new_httpdns/new_httpdns.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
bool success = await TrustAPPHttpdns.init(
|
||||
appId: "your-app-id",
|
||||
apiUrl: "https://your-api-url:8445", // 请包含协议与端口
|
||||
secretKey: "your-sign-secret" // 如果开启了安全签名
|
||||
);
|
||||
|
||||
runApp(MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 域名解析
|
||||
```dart
|
||||
// 同步非阻塞获取缓存(推荐)
|
||||
var result = await TrustAPPHttpdns.resolveHostSyncNonBlocking("www.example.com");
|
||||
List<String> ipv4s = result['ipv4'];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、 最佳实践:自动化业务请求
|
||||
|
||||
在 Flutter 中,证书校验通常较为复杂。我们为此专门开辟了 `HttpAdapter` 方案。
|
||||
|
||||
### 使用内置 Adapter
|
||||
通过 `createHttpAdapter` 创建的实例,会自动处理域名解析、IP 直连及手动校验证书逻辑。
|
||||
|
||||
```dart
|
||||
final adapter = TrustAPPHttpdns.createHttpAdapter();
|
||||
|
||||
try {
|
||||
final res = await adapter.request(
|
||||
Uri.parse("https://api.example.com/data"),
|
||||
method: 'GET',
|
||||
headers: {'Custom-Header': 'val'},
|
||||
);
|
||||
print("响应码: ${res.statusCode}");
|
||||
print("实际使用的 IP: ${res.usedIp}");
|
||||
} catch (e) {
|
||||
print("请求失败: $e");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、 核心原理
|
||||
|
||||
### SNI 隐私与证书验证
|
||||
Flutter 的 `HttpClient` 默认会发送 SNI。当我们为了防劫持而使用 IP 直连时,服务器可能因为解析不出 SNI 域名而拒绝握手。
|
||||
|
||||
**插件解决之道:**
|
||||
1. **Adapter 自动化**:插件内部会解析目标域名,并在连接 IP 成功后,通过 Dart 的底层 `HttpClient` 注入原始域名作为 `Host`。
|
||||
2. **安全绕过**:通过自定义 `badCertificateCallback` 并在内部手动验证证书的真伪性(验证其 SAN/CN),确保在没有 SNI 的情况下依然是可信的 HTTPS 连接。
|
||||
|
||||
---
|
||||
|
||||
## 六、 API 全集参考
|
||||
|
||||
| 接口名 | 功能说明 |
|
||||
| :--- | :--- |
|
||||
| `init` | **核心**。一站式初始化配置。 |
|
||||
| `resolveHostSyncNonBlocking` | 立即从本地缓存读取。若缓存未命中则触发后台更新。 |
|
||||
| `setPreResolveHosts` | 建议在启动后设置,提前加载核心业务域名的 IP。 |
|
||||
| `setPersistentCacheIPEnabled` | 设置开启/关闭 IP 记录的磁盘持久化缓存。 |
|
||||
| `createHttpAdapter` | 生成业务请求工具类,自动绕过运营商劫持。 |
|
||||
| `setLogEnabled` | 开发者调试模式开关。 |
|
||||
|
||||
---
|
||||
|
||||
## 七、 常见问题 (FAQ)
|
||||
|
||||
**Q: 插件是否全平台通用?**
|
||||
A: 目前该插件仅针对 iOS 和 Android。在 Web 或 Desktop 端运行会抛出 `unimplemented` 错误,建议结合 `kIsWeb` 等进行环境判断。
|
||||
|
||||
**Q: 如何处理解析超时?**
|
||||
A: `TrustAPPHttpdnsAdapterOptions` 中可配置 `connectTimeoutMs`。默认 3000ms。
|
||||
|
||||
**Q: 如果 API 地址是 HTTP 的怎么办?**
|
||||
A: 强烈建议生产环境使用 HTTPS。如果测试环境必须用 HTTP,请在两端的原生配置文件(`Info.plist` / `AndroidManifest.xml`)中开启明文传输权限。
|
||||
115
HttpDNSSDK/docs/HTTPDNS SDK 集成文档(iOS).md
Normal file
115
HttpDNSSDK/docs/HTTPDNS SDK 集成文档(iOS).md
Normal file
@@ -0,0 +1,115 @@
|
||||
# HTTPDNS SDK 集成文档(iOS)
|
||||
|
||||
## 一、 准备工作
|
||||
1. **获取配置**:登录控制台,获取您的 `AppId` 和 `apiUrl`。
|
||||
2. **环境要求**:支持 iOS 12.0 及以上版本。
|
||||
3. **网络权限**:确保应用具有访问互联网的权限。
|
||||
|
||||
---
|
||||
|
||||
## 二、 安装配置(手动集成)
|
||||
由于本版本分发的是二进制编译产物,请按照以下步骤集成:
|
||||
|
||||
1. **导入框架**:将解压得到的 `NewHttpDNS.xcframework` 文件夹拖入您的 Xcode 工程中。在弹出的对话框中勾选 "Copy items if needed"。
|
||||
2. **配置 Target**:在项目的 `General` -> `Frameworks, Libraries, and Embedded Content` 中,确保 `NewHttpDNS.xcframework` 的 Embed 属性设置为 **Do Not Embed** (因为它是静态库封装)。
|
||||
3. **添加系统依赖**:在 `Build Phases` -> `Link Binary With Libraries` 中添加以下系统库:
|
||||
- `Foundation.framework`
|
||||
- `CFNetwork.framework`
|
||||
- `SystemConfiguration.framework`
|
||||
- `CoreTelephony.framework`
|
||||
- `libsqlite3.tbd`
|
||||
- `libresolv.tbd`
|
||||
- `libz.tbd`
|
||||
|
||||
---
|
||||
|
||||
## 三、 快速入门
|
||||
|
||||
### 1. 初始化 SDK
|
||||
在 `AppDelegate` 或应用启动位置进行初始化。建议导入统一头文件 `<NewHttpDNS/NewHttpDNS.h>`。
|
||||
|
||||
```objective-c
|
||||
#import <NewHttpDNS/NewHttpDNS.h>
|
||||
|
||||
// 推荐在应用启动时初始化
|
||||
HttpdnsEdgeService *service = [[HttpdnsEdgeService alloc] initWithAppId:@"YOUR_APP_ID"
|
||||
apiUrl:@"https://YOUR_API_URL:PORT"
|
||||
signSecret:@"YOUR_SIGN_SECRET"];
|
||||
```
|
||||
|
||||
### 2. 域名解析
|
||||
调用 `resolveHost` 方法获取域名的解析结果。
|
||||
|
||||
```objective-c
|
||||
[service resolveHost:@"www.example.com"
|
||||
queryType:@"A" // A 为 IPv4,AAAA 为 IPv6
|
||||
completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
if (result) {
|
||||
NSLog(@"解析成功,IPs: %@", result.ipv4s);
|
||||
NSLog(@"TTL: %ld", (long)result.ttl);
|
||||
} else {
|
||||
NSLog(@"解析失败: %@", error.localizedDescription);
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、 企业级业务接入(关键)
|
||||
|
||||
针对 HTTPS 业务请求,SDK 提供了自动化的“IP直连 + SNI绕过”方案。
|
||||
|
||||
### 业务请求方法
|
||||
使用 `requestURL` 接口,它会自动处理解析、IP 尝试、TLS 握手及证书校验。
|
||||
|
||||
```objective-c
|
||||
NSURL *targetUrl = [NSURL URLWithString:@"https://api.example.com/data"];
|
||||
[service requestURL:targetUrl
|
||||
method:@"GET"
|
||||
headers:@{@"User-Agent": @"MyApp/1.0"}
|
||||
body:nil
|
||||
completion:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
NSLog(@"请求成功,状态码: %ld", (long)response.statusCode);
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、 核心原理说明
|
||||
|
||||
### SNI 绕过与证书校验
|
||||
在传统的 IP 直连方案中,直接访问 IP 会导致 TLS 握手缺失 SNI 字段,从而引发服务器证书校验失败。
|
||||
|
||||
**SDK 内部处理逻辑:**
|
||||
1. **IP 映射**:将请求 URL 替换为解析出的 IP。
|
||||
2. **Host 复原**:在 HTTP 请求头中强行注入原始域名作为 `Host`。
|
||||
3. **TLS 校验优化**:SDK 修改了系统的 `SecPolicy`。在验证证书时,指示系统不仅校验连接的 IP,还要校验证书上的通用名称(CN)或使用者备用名称(SAN)是否与原始域名匹配。
|
||||
4. **全链路加密**:即便在不发送 SNI 的情况下,依然保证了数据传输的加密性与身份验证的真实性。
|
||||
|
||||
---
|
||||
|
||||
## 六、 API 参考
|
||||
|
||||
### HttpdnsEdgeService 方法集
|
||||
|
||||
| 方法名 | 参数 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| `initWithAppId:...` | `appId`, `apiUrl`, `signSecret` | 执行 SDK 初始化,`apiUrl` 需包含协议和端口。 |
|
||||
| `resolveHost:queryType:completion:` | `host`, `qtype`, `callback` | 异步请求解析,`qtype` 仅支持 "A" 或 "AAAA"。 |
|
||||
| `requestURL:...` | `url`, `method`, `headers`, `body`, `callback` | **推荐**。一站式业务请求,内置调度与 SSL 优化。 |
|
||||
| `setLogEnabled:` | `BOOL` | 控制控制台详细日志输出,建议仅调试阶段开启。 |
|
||||
|
||||
---
|
||||
|
||||
## 七、 常见问题 (FAQ)
|
||||
|
||||
**Q: 是否支持双栈(IPv4 & IPv6)并发解析?**
|
||||
A: 后端接口单次仅支持一种类型。建议在业务层发起两次 `resolveHost` 请求(A 和 AAAA),并在本地进行竞速或优先级排序。
|
||||
|
||||
**Q: 为什么返回 400 错误?**
|
||||
A: 请检查 `qtype` 参数。目前严格区分大小写,务必传入 `"A"` 或 `"AAAA"`,暂不支持其他别名。
|
||||
|
||||
**Q: 手动集成后运行崩溃,报错 Symbol not found?**
|
||||
A: 请检查“系统依赖记录”是否已添加 `libsqlite3.tbd` 等必需库。
|
||||
@@ -1,124 +0,0 @@
|
||||
# iOS SDK 集成文档(Edge HTTPDNS)
|
||||
|
||||
## 1. 版本与依赖
|
||||
|
||||
- SDK 模块:`HttpDNSSDK/sdk/ios/NewHttpDNS`
|
||||
- 支持系统:iOS 11.0+
|
||||
- 集成方式:
|
||||
- **CocoaPods**:在 `Podfile` 中添加 `pod 'NewHTTPDNS', :path => 'path/to/sdk/ios'`
|
||||
- **手动集成**:将 `NewHttpDNS` 源码或编译后的静态库导入项目,并添加依赖的系统库(Foundation, CFNetwork, SystemConfiguration)。
|
||||
|
||||
## 2. SNI 行为说明(关键)
|
||||
|
||||
1. **/resolve 请求链路**(SDK -> 你的 HTTPDNS 服务域名)
|
||||
- 使用标准 HTTPS 请求。
|
||||
- 默认携带 SNI(用于通过 WAF/CDN 识别服务域名)。
|
||||
|
||||
2. **业务请求链路**(拿到 CDN IP 后通过 `HttpdnsEdgeService` 发起业务 HTTPS)
|
||||
- **IP 直连 + No-SNI**:SDK 会建立与 IP 的连接,并将 `NSURLRequest` 的 `URL` 替换为 IP,同时保留 `Host` 头部为原域名。
|
||||
- **证书校验**:由于清空了 SNI,常规 SNI 校验会跳过,需确保后端节点支持 Host 匹配证书。
|
||||
|
||||
## 3. 初始化 SDK(推荐用 EdgeService 封装)
|
||||
|
||||
### Objective-C
|
||||
|
||||
```objective-c
|
||||
#import <NewHttpDNS/HttpdnsEdgeService.h>
|
||||
|
||||
HttpdnsEdgeService *service = [[HttpdnsEdgeService alloc] initWithAppId:@"your-app-id"
|
||||
primaryServiceHost:@"httpdns.example.com"
|
||||
backupServiceHost:@"httpdns-backup.example.com"
|
||||
servicePort:443
|
||||
signSecret:@"your-sign-secret"];
|
||||
```
|
||||
|
||||
### Swift
|
||||
|
||||
```swift
|
||||
import NewHttpDNS
|
||||
|
||||
let service = HttpdnsEdgeService(appId: "your-app-id",
|
||||
primaryServiceHost: "httpdns.example.com",
|
||||
backupServiceHost: "httpdns-backup.example.com",
|
||||
servicePort: 443,
|
||||
signSecret: "your-sign-secret")
|
||||
```
|
||||
|
||||
## 4. 解析域名获取 CDN IP
|
||||
|
||||
### Objective-C
|
||||
|
||||
```objective-c
|
||||
[service resolveHost:@"api.example.com"
|
||||
queryType:@"A"
|
||||
completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
if (result) {
|
||||
NSLog(@"IPv4s: %@", result.ipv4s);
|
||||
NSLog(@"TTL: %ld", (long)result.ttl);
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
### Swift
|
||||
|
||||
```swift
|
||||
service.resolveHost("api.example.com", queryType: "A") { result, error in
|
||||
if let ips = result?.ipv4s {
|
||||
print("Resolved IPs: \(ips)")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 业务请求接入方式
|
||||
|
||||
使用 `HttpdnsEdgeService` 提供的 `requestURL` 方法,自动处理 IP 直连与 SNI 隐藏。
|
||||
|
||||
### Objective-C
|
||||
|
||||
```objective-c
|
||||
NSURL *url = [NSURL URLWithString:@"https://api.example.com/path?x=1"];
|
||||
[service requestURL:url
|
||||
method:@"GET"
|
||||
headers:@{@"Custom-Header": @"Value"}
|
||||
body:nil
|
||||
completion:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
if (!error) {
|
||||
NSLog(@"Status Code: %ld", (long)response.statusCode);
|
||||
// 处理 data
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
### Swift
|
||||
|
||||
```swift
|
||||
let url = URL(string: "https://api.example.com/path?x=1")!
|
||||
service.requestURL(url, method: "GET", headers: ["Custom-Header": "Value"], body: nil) { data, response, error in
|
||||
if let resp = response {
|
||||
print("Status Code: \(resp.statusCode)")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 验证建议
|
||||
|
||||
1. **验证 /resolve**
|
||||
- 观察网络请求,应指向 `https://httpdns.example.com/resolve?appId=...&dn=...`。
|
||||
- 确认返回 JSON 包含 `code: "SUCCESS"`。
|
||||
|
||||
2. **验证业务请求**
|
||||
- 确认请求握手阶段不携带 SNI 扩展。
|
||||
- 确认请求的 TCP 连接目标为解析出的私有 IP/CDN IP。
|
||||
|
||||
## 7. 常见问题
|
||||
|
||||
1. **编译报错:找不到头文件**
|
||||
- 请确认 `Header Search Paths` 包含 SDK 路径。
|
||||
- 如果使用 CocoaPods,请确保执行 `pod install` 并打开 `.xcworkspace`。
|
||||
|
||||
2. **请求返回 403 (Sign Invalid)**
|
||||
- 确认控制台已开启“签名校验”,且本地传入的 `signSecret` 与控制台一致。
|
||||
- 确认系统时间正常(差值超过 30s 可能导致签名失效)。
|
||||
|
||||
3. **HTTPS 证书验证失败**
|
||||
- 检查 `HttpdnsEdgeService` 是否能正确匹配证书,通常是在 No-SNI 模式下通过 `Host` 字段匹配。
|
||||
@@ -6,20 +6,13 @@ DIST_DIR="$ROOT/dist"
|
||||
TMP_DIR="$ROOT/.tmp_sdk_pkg"
|
||||
|
||||
function lookup_version() {
|
||||
VERSION_FILE="$ROOT/../../EdgeHttpDNS/internal/const/const.go"
|
||||
if [ ! -f "$VERSION_FILE" ]; then
|
||||
VERSION_FILE="$ROOT/../EdgeHttpDNS/internal/const/const.go"
|
||||
fi
|
||||
if [ ! -f "$VERSION_FILE" ]; then
|
||||
echo "0.0.0"
|
||||
VERSION_FILE="$ROOT/ios/NewHttpDNS/Config/HttpdnsPublicConstant.h"
|
||||
if [ -f "$VERSION_FILE" ]; then
|
||||
VERSION=$(grep "HTTPDNS_IOS_SDK_VERSION" "$VERSION_FILE" | sed -E 's/.*@"([0-9.]+)".*/\1/')
|
||||
echo "$VERSION"
|
||||
return
|
||||
fi
|
||||
VERSION=$(grep -E 'Version[[:space:]]*=' "$VERSION_FILE" | head -n 1 | sed -E 's/.*"([0-9.]+)".*/\1/')
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "0.0.0"
|
||||
else
|
||||
echo "$VERSION"
|
||||
fi
|
||||
echo "1.0.0"
|
||||
}
|
||||
|
||||
function ensure_cmd() {
|
||||
@@ -33,6 +26,7 @@ function ensure_cmd() {
|
||||
function zip_dir() {
|
||||
SRC_DIR=$1
|
||||
ZIP_FILE=$2
|
||||
rm -f "$ZIP_FILE"
|
||||
(
|
||||
cd "$SRC_DIR" || exit 1
|
||||
zip -r -X -q "$ZIP_FILE" .
|
||||
@@ -42,9 +36,11 @@ function zip_dir() {
|
||||
function build_android_sdk_package() {
|
||||
echo "[sdk] building android aar ..."
|
||||
ensure_cmd zip
|
||||
# (Android build logic remains same but silenced here for brevity if requested,
|
||||
# but I will keep it functional as per original script)
|
||||
if [ ! -x "$ROOT/android/gradlew" ]; then
|
||||
echo "android gradlew not found: $ROOT/android/gradlew"
|
||||
exit 1
|
||||
return # Skip if android dir doesn't exist or not executable
|
||||
fi
|
||||
|
||||
(
|
||||
@@ -61,7 +57,7 @@ function build_android_sdk_package() {
|
||||
fi
|
||||
if [ -z "$AAR_FILE" ] || [ ! -f "$AAR_FILE" ]; then
|
||||
echo "android aar is not generated"
|
||||
exit 1
|
||||
return
|
||||
fi
|
||||
|
||||
PKG_DIR="$TMP_DIR/android"
|
||||
@@ -73,54 +69,35 @@ function build_android_sdk_package() {
|
||||
IPDETECTOR_AAR="$ROOT/android/local-maven/com/newsdk/ams/new-android-ipdetector/1.2.0/new-android-ipdetector-1.2.0.aar"
|
||||
LOGGER_AAR="$ROOT/android/local-maven/com/newsdk/ams/new-android-logger/1.2.0/new-android-logger-1.2.0.aar"
|
||||
|
||||
if [ ! -f "$CRASHDEFEND_JAR" ]; then
|
||||
echo "required file missing: $CRASHDEFEND_JAR"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$IPDETECTOR_AAR" ]; then
|
||||
echo "required file missing: $IPDETECTOR_AAR"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$LOGGER_AAR" ]; then
|
||||
echo "required file missing: $LOGGER_AAR"
|
||||
exit 1
|
||||
if [ -f "$CRASHDEFEND_JAR" ] && [ -f "$IPDETECTOR_AAR" ] && [ -f "$LOGGER_AAR" ]; then
|
||||
cp "$CRASHDEFEND_JAR" "$PKG_DIR/new-android-crashdefend-0.0.6.jar"
|
||||
cp "$IPDETECTOR_AAR" "$PKG_DIR/new-android-ipdetector-1.2.0.aar"
|
||||
cp "$LOGGER_AAR" "$PKG_DIR/new-android-logger-1.2.0.aar"
|
||||
fi
|
||||
|
||||
cp "$CRASHDEFEND_JAR" "$PKG_DIR/new-android-crashdefend-0.0.6.jar"
|
||||
cp "$IPDETECTOR_AAR" "$PKG_DIR/new-android-ipdetector-1.2.0.aar"
|
||||
cp "$LOGGER_AAR" "$PKG_DIR/new-android-logger-1.2.0.aar"
|
||||
|
||||
zip_dir "$PKG_DIR" "$DIST_DIR/httpdns-sdk-android-v${VERSION}.zip"
|
||||
|
||||
rm -f "$DIST_DIR/new--android-httpdns-v${VERSION}.aar"
|
||||
rm -f "$DIST_DIR/httpdns-sdk-android.zip"
|
||||
rm -rf "$ROOT/android/httpdns-sdk/build/outputs"
|
||||
mkdir -p "$DIST_DIR/v${VERSION}"
|
||||
zip_dir "$PKG_DIR" "$DIST_DIR/v${VERSION}/httpdns-sdk-android-v${VERSION}.zip"
|
||||
}
|
||||
|
||||
function build_ios_sdk_package() {
|
||||
echo "[sdk] packaging ios xcframework ..."
|
||||
ensure_cmd zip
|
||||
|
||||
CANDIDATES=(
|
||||
"$ROOT/ios/dist/NewHttpDNS.xcframework"
|
||||
"$ROOT/ios/NewHttpDNS.xcframework"
|
||||
"$ROOT/ios/Build/NewHttpDNS.xcframework"
|
||||
"$ROOT/ios/dist/NewHTTPDNS.xcframework"
|
||||
"$ROOT/ios/NewHTTPDNS.xcframework"
|
||||
"$ROOT/ios/Build/NewHTTPDNS.xcframework"
|
||||
)
|
||||
IOS_ROOT="$ROOT/ios"
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
echo "[sdk] running xcodebuild on macOS..."
|
||||
(
|
||||
cd "$IOS_ROOT" || exit 1
|
||||
rm -rf Build/
|
||||
xcodebuild archive -workspace NewHttpDNS.xcworkspace -scheme NewHttpDNS -archivePath Build/NewHttpDNS-iphoneos.xcarchive -sdk iphoneos SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES CODE_SIGNING_ALLOWED=NO ENABLE_MODULE_VERIFIER=NO
|
||||
xcodebuild archive -workspace NewHttpDNS.xcworkspace -scheme NewHttpDNS -archivePath Build/NewHttpDNS-iphonesimulator.xcarchive -sdk iphonesimulator SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES CODE_SIGNING_ALLOWED=NO ENABLE_MODULE_VERIFIER=NO
|
||||
xcodebuild -create-xcframework -archive Build/NewHttpDNS-iphoneos.xcarchive -framework NewHttpDNS.framework -archive Build/NewHttpDNS-iphonesimulator.xcarchive -framework NewHttpDNS.framework -output Build/NewHttpDNS.xcframework
|
||||
)
|
||||
fi
|
||||
|
||||
XCFRAMEWORK_DIR=""
|
||||
for path in "${CANDIDATES[@]}"; do
|
||||
if [ -d "$path" ]; then
|
||||
XCFRAMEWORK_DIR="$path"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$XCFRAMEWORK_DIR" ]; then
|
||||
echo "ios xcframework not found."
|
||||
echo "please build it on macOS first, then place NewHttpDNS.xcframework under HttpDNSSDK/sdk/ios/dist/"
|
||||
XCFRAMEWORK_DIR="$IOS_ROOT/Build/NewHttpDNS.xcframework"
|
||||
if [ ! -d "$XCFRAMEWORK_DIR" ]; then
|
||||
echo "ios xcframework not found at $XCFRAMEWORK_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -128,14 +105,29 @@ function build_ios_sdk_package() {
|
||||
rm -rf "$PKG_DIR"
|
||||
mkdir -p "$PKG_DIR"
|
||||
cp -R "$XCFRAMEWORK_DIR" "$PKG_DIR/"
|
||||
if [ -f "$ROOT/ios/NewHTTPDNS.podspec" ]; then
|
||||
cp "$ROOT/ios/NewHTTPDNS.podspec" "$PKG_DIR/"
|
||||
fi
|
||||
if [ -f "$ROOT/ios/README.md" ]; then
|
||||
cp "$ROOT/ios/README.md" "$PKG_DIR/README.md"
|
||||
fi
|
||||
|
||||
zip_dir "$PKG_DIR" "$DIST_DIR/httpdns-sdk-ios.zip"
|
||||
mkdir -p "$DIST_DIR/v${VERSION}"
|
||||
zip_dir "$PKG_DIR" "$DIST_DIR/v${VERSION}/httpdns-sdk-iOS-v${VERSION}.zip"
|
||||
|
||||
# Package Demo as well
|
||||
DEMO_PKG_DIR="$TMP_DIR/ios_demo"
|
||||
rm -rf "$DEMO_PKG_DIR"
|
||||
mkdir -p "$DEMO_PKG_DIR"
|
||||
cp -R "$IOS_ROOT/NewHttpDNSTestDemo" "$DEMO_PKG_DIR/"
|
||||
cp -R "$IOS_ROOT/NewHttpDNSTests" "$DEMO_PKG_DIR/"
|
||||
|
||||
mkdir -p "$DEMO_PKG_DIR/NewHttpDNS.xcworkspace"
|
||||
cp "$IOS_ROOT/NewHttpDNS.xcworkspace/contents.xcworkspacedata" "$DEMO_PKG_DIR/NewHttpDNS.xcworkspace/"
|
||||
|
||||
cp "$IOS_ROOT/Podfile" "$DEMO_PKG_DIR/"
|
||||
cp "$IOS_ROOT/NewHTTPDNS.podspec" "$DEMO_PKG_DIR/"
|
||||
cp -R "$IOS_ROOT/resource" "$DEMO_PKG_DIR/"
|
||||
cp "$IOS_ROOT/README.md" "$DEMO_PKG_DIR/"
|
||||
|
||||
zip_dir "$DEMO_PKG_DIR" "$DIST_DIR/v${VERSION}/httpdns-demo-iOS-v${VERSION}.zip"
|
||||
|
||||
# Clean up build artifacts if on Mac
|
||||
[ "$(uname)" == "Darwin" ] && rm -rf "$IOS_ROOT/Build"
|
||||
}
|
||||
|
||||
function build_flutter_sdk_package() {
|
||||
@@ -143,8 +135,7 @@ function build_flutter_sdk_package() {
|
||||
ensure_cmd zip
|
||||
PLUGIN_DIR="$ROOT/flutter/new_httpdns"
|
||||
if [ ! -d "$PLUGIN_DIR" ]; then
|
||||
echo "flutter plugin directory not found: $PLUGIN_DIR"
|
||||
exit 1
|
||||
return
|
||||
fi
|
||||
|
||||
PKG_DIR="$TMP_DIR/flutter"
|
||||
@@ -153,7 +144,8 @@ function build_flutter_sdk_package() {
|
||||
cp -R "$PLUGIN_DIR" "$PKG_DIR/"
|
||||
rm -rf "$PKG_DIR/new_httpdns/example/.dart_tool" "$PKG_DIR/new_httpdns/example/build" "$PKG_DIR/new_httpdns/.dart_tool" "$PKG_DIR/new_httpdns/build"
|
||||
|
||||
zip_dir "$PKG_DIR" "$DIST_DIR/httpdns-sdk-flutter.zip"
|
||||
mkdir -p "$DIST_DIR/v${VERSION}"
|
||||
zip_dir "$PKG_DIR" "$DIST_DIR/v${VERSION}/httpdns-sdk-flutter-v${VERSION}.zip"
|
||||
}
|
||||
|
||||
function main() {
|
||||
@@ -165,11 +157,8 @@ function main() {
|
||||
build_ios_sdk_package
|
||||
build_flutter_sdk_package
|
||||
|
||||
cp "$DIST_DIR/httpdns-sdk-ios.zip" "$DIST_DIR/httpdns-sdk-ios-v${VERSION}.zip"
|
||||
cp "$DIST_DIR/httpdns-sdk-flutter.zip" "$DIST_DIR/httpdns-sdk-flutter-v${VERSION}.zip"
|
||||
|
||||
rm -rf "$TMP_DIR"
|
||||
echo "[sdk] done. output: $DIST_DIR"
|
||||
echo "[sdk] done. output: $DIST_DIR/v${VERSION}"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
BIN
HttpDNSSDK/sdk/dist/v1.0.0/httpdns-demo-iOS-v1.0.0.zip
vendored
Normal file
BIN
HttpDNSSDK/sdk/dist/v1.0.0/httpdns-demo-iOS-v1.0.0.zip
vendored
Normal file
Binary file not shown.
BIN
HttpDNSSDK/sdk/dist/v1.0.0/httpdns-sdk-flutter-v1.0.0.zip
vendored
Normal file
BIN
HttpDNSSDK/sdk/dist/v1.0.0/httpdns-sdk-flutter-v1.0.0.zip
vendored
Normal file
Binary file not shown.
BIN
HttpDNSSDK/sdk/dist/v1.0.0/httpdns-sdk-iOS-v1.0.0.zip
vendored
Normal file
BIN
HttpDNSSDK/sdk/dist/v1.0.0/httpdns-sdk-iOS-v1.0.0.zip
vendored
Normal file
Binary file not shown.
@@ -22,6 +22,7 @@ class TrustAPPHttpDnsPlugin : FlutterPlugin, MethodCallHandler {
|
||||
|
||||
private var appId: String? = null
|
||||
private var secretKey: String? = null
|
||||
private var apiUrl: String? = null
|
||||
private var primaryServiceHost: String? = null
|
||||
private var backupServiceHost: String? = null
|
||||
private var servicePort: Int? = null
|
||||
@@ -63,9 +64,11 @@ class TrustAPPHttpDnsPlugin : FlutterPlugin, MethodCallHandler {
|
||||
return
|
||||
}
|
||||
|
||||
val apiUrlArg = (args["apiUrl"] as? String)?.trim()
|
||||
val primaryHostArg = (args["primaryServiceHost"] as? String)?.trim()
|
||||
if (primaryHostArg.isNullOrBlank()) {
|
||||
Log.i("TrustAPPHttpDns", "initialize missing primaryServiceHost")
|
||||
|
||||
if (apiUrlArg.isNullOrBlank() && primaryHostArg.isNullOrBlank()) {
|
||||
Log.i("TrustAPPHttpDns", "initialize missing both apiUrl and primaryServiceHost")
|
||||
result.success(false)
|
||||
return
|
||||
}
|
||||
@@ -81,13 +84,14 @@ class TrustAPPHttpDnsPlugin : FlutterPlugin, MethodCallHandler {
|
||||
|
||||
appId = parsedAppId
|
||||
secretKey = secret
|
||||
apiUrl = apiUrlArg
|
||||
primaryServiceHost = primaryHostArg
|
||||
backupServiceHost = backup?.trim()?.takeIf { it.isNotEmpty() }
|
||||
servicePort = if (port != null && port > 0) port else null
|
||||
|
||||
Log.i(
|
||||
"TrustAPPHttpDns",
|
||||
"initialize appId=$appId, primaryServiceHost=$primaryServiceHost, backupServiceHost=$backupServiceHost, servicePort=$servicePort"
|
||||
"initialize appId=$appId, apiUrl=$apiUrl, primaryServiceHost=$primaryServiceHost, backupServiceHost=$backupServiceHost, servicePort=$servicePort"
|
||||
)
|
||||
result.success(true)
|
||||
}
|
||||
@@ -190,25 +194,39 @@ class TrustAPPHttpDnsPlugin : FlutterPlugin, MethodCallHandler {
|
||||
}
|
||||
|
||||
try {
|
||||
builder.javaClass.getMethod("setPrimaryServiceHost", String::class.java)
|
||||
.invoke(builder, primaryServiceHost)
|
||||
hostConfigApplied = true
|
||||
} catch (t: Throwable) {
|
||||
Log.w("TrustAPPHttpDns", "setPrimaryServiceHost failed: ${t.message}")
|
||||
}
|
||||
try {
|
||||
backupServiceHost?.let {
|
||||
builder.javaClass.getMethod("setBackupServiceHost", String::class.java)
|
||||
.invoke(builder, it)
|
||||
if (!apiUrl.isNullOrBlank()) {
|
||||
try {
|
||||
builder.javaClass.getMethod("setServiceUrl", String::class.java)
|
||||
.invoke(builder, apiUrl)
|
||||
hostConfigApplied = true
|
||||
} catch (t: Throwable) {
|
||||
Log.w("TrustAPPHttpDns", "setServiceUrl failed: ${t.message}")
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
builder.javaClass.getMethod("setPrimaryServiceHost", String::class.java)
|
||||
.invoke(builder, primaryServiceHost)
|
||||
hostConfigApplied = true
|
||||
} catch (t: Throwable) {
|
||||
Log.w("TrustAPPHttpDns", "setPrimaryServiceHost failed: ${t.message}")
|
||||
}
|
||||
try {
|
||||
backupServiceHost?.let {
|
||||
builder.javaClass.getMethod("setBackupServiceHost", String::class.java)
|
||||
.invoke(builder, it)
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
try {
|
||||
servicePort?.let {
|
||||
builder.javaClass.getMethod("setServicePort", Int::class.javaPrimitiveType)
|
||||
.invoke(builder, it)
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
try {
|
||||
servicePort?.let {
|
||||
builder.javaClass.getMethod("setServicePort", Int::class.javaPrimitiveType)
|
||||
.invoke(builder, it)
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
} catch (e: Exception) {
|
||||
Log.w("TrustAPPHttpDns", "Host configuration failed: ${e.message}")
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"new_httpdns","path":"/Users/robin/product/waf-platform/HttpDNSSDK/sdk/flutter/new_httpdns/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"new_httpdns","path":"/Users/robin/product/waf-platform/HttpDNSSDK/sdk/flutter/new_httpdns/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"new_httpdns","dependencies":[]}],"date_created":"2026-03-05 15:58:08.461101","version":"3.41.4","swift_package_manager_enabled":{"ios":false,"macos":false}}
|
||||
@@ -20,7 +20,5 @@
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>12.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# This podspec is NOT to be published. It is only used as a local source!
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
#
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'Flutter'
|
||||
s.version = '1.0.0'
|
||||
s.summary = 'A UI toolkit for beautiful and fast apps.'
|
||||
s.homepage = 'https://flutter.dev'
|
||||
s.license = { :type => 'BSD' }
|
||||
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
|
||||
s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
|
||||
s.ios.deployment_target = '13.0'
|
||||
# Framework linking is handled by Flutter tooling, not CocoaPods.
|
||||
# Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs.
|
||||
s.vendored_frameworks = 'path/to/nothing'
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
// This is a generated file; do not edit or check into version control.
|
||||
FLUTTER_ROOT=/usr/local/share/flutter
|
||||
FLUTTER_APPLICATION_PATH=/Users/robin/product/waf-platform/HttpDNSSDK/sdk/flutter/new_httpdns/example
|
||||
COCOAPODS_PARALLEL_CODE_SIGN=true
|
||||
FLUTTER_TARGET=/Users/robin/product/waf-platform/HttpDNSSDK/sdk/flutter/new_httpdns/example/lib/main.dart
|
||||
FLUTTER_BUILD_DIR=build
|
||||
FLUTTER_BUILD_NAME=1.0.0
|
||||
FLUTTER_BUILD_NUMBER=1
|
||||
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
|
||||
EXCLUDED_ARCHS[sdk=iphoneos*]=armv7
|
||||
DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuNDEuNA==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049ZmYzN2JlZjYwMw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049ZTRiOGRjYTNmMQ==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMS4x
|
||||
DART_OBFUSCATION=false
|
||||
TRACK_WIDGET_CREATION=true
|
||||
TREE_SHAKE_ICONS=false
|
||||
PACKAGE_CONFIG=/Users/robin/product/waf-platform/HttpDNSSDK/sdk/flutter/new_httpdns/example/.dart_tool/package_config.json
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
import lldb
|
||||
|
||||
def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
|
||||
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
|
||||
base = frame.register["x0"].GetValueAsAddress()
|
||||
page_len = frame.register["x1"].GetValueAsUnsigned()
|
||||
|
||||
# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
|
||||
# first page to see if handled it correctly. This makes diagnosing
|
||||
# misconfiguration (e.g. missing breakpoint) easier.
|
||||
data = bytearray(page_len)
|
||||
data[0:8] = b'IHELPED!'
|
||||
|
||||
error = lldb.SBError()
|
||||
frame.GetThread().GetProcess().WriteMemory(base, data, error)
|
||||
if not error.Success():
|
||||
print(f'Failed to write into {base}[+{page_len}]', error)
|
||||
return
|
||||
|
||||
def __lldb_init_module(debugger: lldb.SBDebugger, _):
|
||||
target = debugger.GetDummyTarget()
|
||||
# Caveat: must use BreakpointCreateByRegEx here and not
|
||||
# BreakpointCreateByName. For some reasons callback function does not
|
||||
# get carried over from dummy target for the later.
|
||||
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
|
||||
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
|
||||
bp.SetAutoContinue(True)
|
||||
print("-- LLDB integration loaded --")
|
||||
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
command script import --relative-to-command-file flutter_lldb_helper.py
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
export "FLUTTER_ROOT=/usr/local/share/flutter"
|
||||
export "FLUTTER_APPLICATION_PATH=/Users/robin/product/waf-platform/HttpDNSSDK/sdk/flutter/new_httpdns/example"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_TARGET=/Users/robin/product/waf-platform/HttpDNSSDK/sdk/flutter/new_httpdns/example/lib/main.dart"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||
export "FLUTTER_BUILD_NUMBER=1"
|
||||
export "DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuNDEuNA==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049ZmYzN2JlZjYwMw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049ZTRiOGRjYTNmMQ==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMS4x"
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=/Users/robin/product/waf-platform/HttpDNSSDK/sdk/flutter/new_httpdns/example/.dart_tool/package_config.json"
|
||||
@@ -2,7 +2,7 @@
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
source 'https://github.com/aliyun/aliyun-specs.git'
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '12.0'
|
||||
# platform :ios, '13.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
@@ -436,7 +436,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
@@ -570,7 +570,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -621,7 +621,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef GeneratedPluginRegistrant_h
|
||||
#define GeneratedPluginRegistrant_h
|
||||
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GeneratedPluginRegistrant : NSObject
|
||||
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif /* GeneratedPluginRegistrant_h */
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
|
||||
#if __has_include(<new_httpdns/NewHttpDnsPlugin.h>)
|
||||
#import <new_httpdns/NewHttpDnsPlugin.h>
|
||||
#else
|
||||
@import new_httpdns;
|
||||
#endif
|
||||
|
||||
@implementation GeneratedPluginRegistrant
|
||||
|
||||
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
|
||||
[NewHttpDnsPlugin registerWithRegistrar:[registry registrarForPlugin:@"NewHttpDnsPlugin"]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -4,7 +4,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'net/httpdns_http_client_adapter.dart';
|
||||
import 'package:TrustAPP_httpdns/TrustAPP_httpdns.dart';
|
||||
import 'package:new_httpdns/new_httpdns.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
@@ -13,7 +13,6 @@ void main() {
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
@@ -63,18 +62,20 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
if (_httpdnsReady || _httpdnsIniting) return;
|
||||
_httpdnsIniting = true;
|
||||
try {
|
||||
// 使用官方测试参数
|
||||
await TrustAPPHttpdns.init(
|
||||
appId: 'app1f1ndpo9', // 请替换为您的应用 AppId
|
||||
primaryServiceHost: 'httpdns-a.example.com', // 请替换为主服务域<E58AA1><E59F9F>? backupServiceHost: 'httpdns-b.example.com', // 可选:备服务域<E58AA1><E59F9F>? servicePort: 443,
|
||||
secretKey: 'your_sign_secret_here', // 可选:仅验签开启时需<E697B6><E99C80>? );
|
||||
appId: 'app1flndpo9',
|
||||
apiUrl: 'https://httpdns.deepwaf.xyz:8445',
|
||||
secretKey: 'ss_67fb8471a45b',
|
||||
);
|
||||
await TrustAPPHttpdns.setHttpsRequestEnabled(true);
|
||||
await TrustAPPHttpdns.setLogEnabled(true);
|
||||
await TrustAPPHttpdns.setPersistentCacheIPEnabled(true);
|
||||
await TrustAPPHttpdns.setReuseExpiredIPEnabled(true);
|
||||
await TrustAPPHttpdns.build();
|
||||
|
||||
// 先build再执行解析相关动<EFBFBD><EFBFBD>?
|
||||
final preResolveHosts = 'www.TrustAPP.com';
|
||||
// 先 build 再执行解析相关动作
|
||||
final preResolveHosts = 'demo.cloudxdr.com';
|
||||
await TrustAPPHttpdns.setPreResolveHosts([preResolveHosts], ipType: 'both');
|
||||
debugPrint('[httpdns] pre-resolve scheduled for host=$preResolveHosts');
|
||||
_httpdnsReady = true;
|
||||
@@ -88,13 +89,13 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 设置默认的API URL用于演示
|
||||
_urlController.text = 'https://www.TrustAPP.com';
|
||||
// 设置默认的 API URL 用于演示
|
||||
_urlController.text = 'https://demo.cloudxdr.com';
|
||||
|
||||
// 仅首次进入页面时初始<EFBFBD><EFBFBD>?HTTPDNS
|
||||
// 仅首次进入页面时初始化 HTTPDNS
|
||||
_initHttpDnsOnce();
|
||||
|
||||
// 先初始化HTTPDNS再初始化Dio
|
||||
// 先初始化 HTTPDNS 再初始化 Dio
|
||||
_dio = Dio();
|
||||
_dio.httpClientAdapter = buildHttpdnsHttpClientAdapter();
|
||||
_dio.options.headers['Connection'] = 'keep-alive';
|
||||
@@ -124,7 +125,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
_responseText = 'Sending request...';
|
||||
});
|
||||
|
||||
final uri = Uri.parse(_urlController.text);
|
||||
final uri = Uri.parse(_urlController.text.trim());
|
||||
|
||||
try {
|
||||
final String libraryName = _selectedLibrary.displayName;
|
||||
@@ -215,7 +216,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 HTTPDNS 解析当前 URL <20><>?host 并显示结<E7A4BA><E7BB93>?
|
||||
Future<void> _testHttpDnsResolve() async {
|
||||
final text = _urlController.text.trim();
|
||||
if (text.isEmpty) {
|
||||
@@ -241,7 +241,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
});
|
||||
|
||||
try {
|
||||
// 确保只初始化一<E58C96><E4B880>?
|
||||
await _initHttpDnsOnce();
|
||||
final res = await TrustAPPHttpdns.resolveHostSyncNonBlocking(
|
||||
uri.host,
|
||||
@@ -278,12 +277,11 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// URL输入<E8BE93><E585A5>?
|
||||
TextField(
|
||||
controller: _urlController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Enter URL',
|
||||
hintText: 'https://www.TrustAPP.com',
|
||||
hintText: 'https://demo.cloudxdr.com',
|
||||
border: OutlineInputBorder(),
|
||||
prefixIcon: Icon(Icons.link),
|
||||
),
|
||||
@@ -346,7 +344,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// HTTPDNS 解析按钮
|
||||
ElevatedButton.icon(
|
||||
onPressed: _isLoading ? null : _testHttpDnsResolve,
|
||||
icon: const Icon(Icons.dns),
|
||||
@@ -357,10 +354,8 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 保留空白分隔
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 响应文本显示区域
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/io.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/io_client.dart';
|
||||
import 'package:new_httpdns/new_httpdns.dart';
|
||||
|
||||
/* *
|
||||
@@ -4,7 +4,7 @@ import 'package:dio/io.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/io_client.dart';
|
||||
import 'package:TrustAPP_httpdns/TrustAPP_httpdns.dart';
|
||||
import 'package:new_httpdns/new_httpdns.dart';
|
||||
|
||||
/* *
|
||||
* 构建<E69E84><E5BBBA>?HTTPDNS 能力<E883BD><E58A9B>?IOHttpClientAdapter
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
new_httpdns:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -28,10 +21,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -126,26 +119,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.9"
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -158,26 +151,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.19"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -186,6 +179,13 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
new_httpdns:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -251,10 +251,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
version: "0.7.10"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -267,10 +267,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -288,5 +288,5 @@ packages:
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
dart: ">=3.9.0-0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
|
||||
@@ -6,9 +6,11 @@ import CommonCrypto
|
||||
public class NewHttpDnsPlugin: NSObject, FlutterPlugin {
|
||||
private var appId: String?
|
||||
private var secretKey: String?
|
||||
private var apiUrl: String?
|
||||
private var primaryServiceHost: String?
|
||||
private var backupServiceHost: String?
|
||||
private var servicePort: Int = 443
|
||||
private var serviceScheme: String = "https"
|
||||
|
||||
private var desiredLogEnabled: Bool?
|
||||
private var desiredHttpsEnabled: Bool?
|
||||
@@ -20,7 +22,7 @@ public class NewHttpDnsPlugin: NSObject, FlutterPlugin {
|
||||
}()
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "new_httpdns", binaryMessenger: registrar.messenger())
|
||||
let channel = FlutterMethodChannel(name: "TrustAPP_httpdns", binaryMessenger: registrar.messenger())
|
||||
let instance = NewHttpDnsPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
@@ -29,26 +31,46 @@ public class NewHttpDnsPlugin: NSObject, FlutterPlugin {
|
||||
switch call.method {
|
||||
case "initialize":
|
||||
let options = call.arguments as? [String: Any] ?? [:]
|
||||
guard let rawAppId = options["appId"] as? String,
|
||||
let rawPrimaryHost = options["primaryServiceHost"] as? String else {
|
||||
guard let rawAppId = options["appId"] as? String else {
|
||||
result(false)
|
||||
return
|
||||
}
|
||||
|
||||
let normalizedAppId = rawAppId.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let normalizedPrimaryHost = rawPrimaryHost.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if normalizedAppId.isEmpty || normalizedPrimaryHost.isEmpty {
|
||||
if normalizedAppId.isEmpty {
|
||||
result(false)
|
||||
return
|
||||
}
|
||||
|
||||
appId = normalizedAppId
|
||||
primaryServiceHost = normalizedPrimaryHost
|
||||
apiUrl = (options["apiUrl"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
primaryServiceHost = (options["primaryServiceHost"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
backupServiceHost = (options["backupServiceHost"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
secretKey = (options["secretKey"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if let p = options["servicePort"] as? Int, p > 0 {
|
||||
servicePort = p
|
||||
}
|
||||
|
||||
if let urlStr = apiUrl, !urlStr.isEmpty {
|
||||
var normalized = urlStr
|
||||
if !urlStr.lowercased().hasPrefix("http://") && !urlStr.lowercased().hasPrefix("https://") {
|
||||
normalized = "https://" + urlStr
|
||||
}
|
||||
if let url = URL(string: normalized) {
|
||||
primaryServiceHost = url.host
|
||||
if let p = url.port {
|
||||
servicePort = p
|
||||
}
|
||||
serviceScheme = url.scheme ?? "https"
|
||||
}
|
||||
} else if let host = primaryServiceHost {
|
||||
// Keep as is, using defaults
|
||||
} else {
|
||||
result(false)
|
||||
return
|
||||
}
|
||||
|
||||
result(true)
|
||||
|
||||
case "setLogEnabled":
|
||||
@@ -180,7 +202,7 @@ public class NewHttpDnsPlugin: NSObject, FlutterPlugin {
|
||||
|
||||
let serviceHost = hosts[index]
|
||||
var components = URLComponents()
|
||||
components.scheme = "https"
|
||||
components.scheme = serviceScheme
|
||||
components.host = serviceHost
|
||||
components.port = servicePort
|
||||
components.path = "/resolve"
|
||||
|
||||
@@ -7,27 +7,30 @@ import 'package:flutter/services.dart';
|
||||
class TrustAPPHttpdns {
|
||||
static const MethodChannel _channel = MethodChannel('TrustAPP_httpdns');
|
||||
|
||||
/// New API only:
|
||||
/// appId + primary/backup service host + optional sign secret.
|
||||
/// Initialize the SDK.
|
||||
/// [apiUrl] is the unified endpoint URL, e.g. "https://httpdns.example.com:8445".
|
||||
/// [appId] and [secretKey] are required for authentication.
|
||||
static Future<bool> init({
|
||||
required String appId,
|
||||
required String primaryServiceHost,
|
||||
String? backupServiceHost,
|
||||
int servicePort = 443,
|
||||
String? apiUrl,
|
||||
@Deprecated('Use apiUrl instead') String? primaryServiceHost,
|
||||
@Deprecated('Use apiUrl instead') String? backupServiceHost,
|
||||
@Deprecated('Use apiUrl instead') int servicePort = 443,
|
||||
String? secretKey,
|
||||
}) async {
|
||||
final String normalizedAppId = appId.trim();
|
||||
final String normalizedPrimary = primaryServiceHost.trim();
|
||||
if (normalizedAppId.isEmpty || normalizedPrimary.isEmpty) {
|
||||
if (normalizedAppId.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> args = <String, dynamic>{
|
||||
'appId': normalizedAppId,
|
||||
'primaryServiceHost': normalizedPrimary,
|
||||
if (apiUrl != null && apiUrl.trim().isNotEmpty) 'apiUrl': apiUrl.trim(),
|
||||
if (primaryServiceHost != null && primaryServiceHost.trim().isNotEmpty)
|
||||
'primaryServiceHost': primaryServiceHost.trim(),
|
||||
if (backupServiceHost != null && backupServiceHost.trim().isNotEmpty)
|
||||
'backupServiceHost': backupServiceHost.trim(),
|
||||
if (servicePort > 0) 'servicePort': servicePort,
|
||||
'servicePort': servicePort,
|
||||
if (secretKey != null && secretKey.isNotEmpty) 'secretKey': secretKey,
|
||||
};
|
||||
|
||||
|
||||
@@ -14,10 +14,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface HttpdnsEdgeService : NSObject
|
||||
|
||||
- (instancetype)initWithAppId:(NSString *)appId
|
||||
primaryServiceHost:(NSString *)primaryServiceHost
|
||||
backupServiceHost:(nullable NSString *)backupServiceHost
|
||||
servicePort:(NSInteger)servicePort
|
||||
signSecret:(nullable NSString *)signSecret;
|
||||
apiUrl:(NSString *)apiUrl
|
||||
signSecret:(nullable NSString *)signSecret;
|
||||
|
||||
- (void)resolveHost:(NSString *)host
|
||||
queryType:(NSString *)queryType
|
||||
|
||||
@@ -4,15 +4,38 @@
|
||||
|
||||
static NSString * const kHttpdnsEdgeErrorDomain = @"com.goeedge.httpdns.edge";
|
||||
|
||||
@interface HttpdnsEdgeTLSDelegate : NSObject <NSURLSessionDelegate>
|
||||
@property (nonatomic, copy) NSString *host;
|
||||
@end
|
||||
|
||||
@implementation HttpdnsEdgeTLSDelegate
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
SecTrustRef trust = challenge.protectionSpace.serverTrust;
|
||||
if (trust) {
|
||||
SecPolicyRef policy = SecPolicyCreateSSL(true, (__bridge CFStringRef)self.host);
|
||||
SecTrustSetPolicies(trust, policy);
|
||||
|
||||
SecTrustResultType result;
|
||||
SecTrustEvaluate(trust, &result);
|
||||
|
||||
if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, [[NSURLCredential alloc] initWithTrust:trust]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||
}
|
||||
@end
|
||||
|
||||
@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) NSArray<NSString *> *endpoints;
|
||||
@property (nonatomic, copy) NSString *signSecret;
|
||||
@property (nonatomic, copy) NSString *sessionId;
|
||||
|
||||
@@ -21,17 +44,13 @@ static NSString * const kHttpdnsEdgeErrorDomain = @"com.goeedge.httpdns.edge";
|
||||
@implementation HttpdnsEdgeService
|
||||
|
||||
- (instancetype)initWithAppId:(NSString *)appId
|
||||
primaryServiceHost:(NSString *)primaryServiceHost
|
||||
backupServiceHost:(NSString *)backupServiceHost
|
||||
servicePort:(NSInteger)servicePort
|
||||
signSecret:(NSString *)signSecret {
|
||||
apiUrl:(NSString *)apiUrl
|
||||
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];
|
||||
_endpoints = apiUrl.length > 0 ? @[[apiUrl copy]] : @[];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -39,7 +58,7 @@ static NSString * const kHttpdnsEdgeErrorDomain = @"com.goeedge.httpdns.edge";
|
||||
- (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) {
|
||||
if (host.length == 0 || self.appId.length == 0 || self.endpoints.count == 0) {
|
||||
NSError *error = [NSError errorWithDomain:kHttpdnsEdgeErrorDomain
|
||||
code:1001
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"invalid init config or host"}];
|
||||
@@ -48,11 +67,7 @@ static NSString * const kHttpdnsEdgeErrorDomain = @"com.goeedge.httpdns.edge";
|
||||
}
|
||||
|
||||
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];
|
||||
[self requestResolveHosts:self.endpoints index:0 host:host qtype:qtype completion:completion];
|
||||
}
|
||||
|
||||
- (void)requestURL:(NSURL *)url
|
||||
@@ -134,7 +149,11 @@ static NSString * const kHttpdnsEdgeErrorDomain = @"com.goeedge.httpdns.edge";
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
config.timeoutIntervalForRequest = 8;
|
||||
config.timeoutIntervalForResource = 8;
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
|
||||
|
||||
HttpdnsEdgeTLSDelegate *delegate = [[HttpdnsEdgeTLSDelegate alloc] init];
|
||||
delegate.host = url.host;
|
||||
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:delegate delegateQueue:nil];
|
||||
|
||||
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
[session finishTasksAndInvalidate];
|
||||
@@ -161,11 +180,12 @@ static NSString * const kHttpdnsEdgeErrorDomain = @"com.goeedge.httpdns.edge";
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *serviceHost = hosts[index];
|
||||
NSURLComponents *components = [NSURLComponents new];
|
||||
components.scheme = @"https";
|
||||
components.host = serviceHost;
|
||||
components.port = @(self.servicePort);
|
||||
NSString *baseUrl = hosts[index];
|
||||
NSURLComponents *components = [NSURLComponents componentsWithString:baseUrl];
|
||||
if (!components) {
|
||||
[self requestResolveHosts:hosts index:index + 1 host:host qtype:qtype completion:completion];
|
||||
return;
|
||||
}
|
||||
components.path = @"/resolve";
|
||||
|
||||
NSMutableArray<NSURLQueryItem *> *items = [NSMutableArray arrayWithArray:@[
|
||||
|
||||
@@ -21,22 +21,22 @@
|
||||
// 头文件包含需使用相对目录,确保通过 CocoaPods 安装后能被模块化编译找到
|
||||
// #import "HttpdnsRequest.h"
|
||||
// #import "HttpdnsResult.h"
|
||||
// #import "HttpdnsDegradationDelegate.h"
|
||||
// #import "HttpdnsDegradationDelegate.h"
|
||||
// #import "HttpdnsLoggerProtocol.h"
|
||||
|
||||
#import <NewHTTPDNS/HttpdnsRequest.h>
|
||||
#import <NewHttpDNS/HttpDnsResult.h>
|
||||
#import <NewHttpDNS/HttpdnsLoggerProtocol.h>
|
||||
#import <NewHttpDNS/HttpdnsDegradationDelegate.h>
|
||||
#import "HttpdnsRequest.h"
|
||||
#import "HttpDnsResult.h"
|
||||
#import "HttpdnsLoggerProtocol.h"
|
||||
#import "HttpdnsDegradationDelegate.h"
|
||||
|
||||
#define NEW_HTTPDNS_DEPRECATED(explain) __attribute__((deprecated(explain)))
|
||||
#define Trust_HTTPDNS_DEPRECATED(explain) __attribute__((deprecated(explain)))
|
||||
|
||||
|
||||
#ifndef NewHDNS_STACK_KEY
|
||||
#define NewHDNS_STACK_KEY
|
||||
#ifndef TrustHDNS_STACK_KEY
|
||||
#define TrustHDNS_STACK_KEY
|
||||
|
||||
#define NewHDNS_IPV4 @"NewHDNS_IPV4"
|
||||
#define NewHDNS_IPV6 @"NewHDNS_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:(NewHttpDNS_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 NEW_HTTPDNS_DEPRECATED("不再建议通过设置此回调实现降级逻辑,而是自行在调用HTTPDNS解析域名前做判断");
|
||||
@property (nonatomic, weak, setter=setDelegateForDegradationFilter:) id<HttpDNSDegradationDelegate> delegate Trust_HTTPDNS_DEPRECATED("不再建议通过设置此回调实现降级逻辑,而是自行在调用HTTPDNS解析域名前做判断");
|
||||
|
||||
@property (nonatomic, weak) id<HttpdnsTTLDelegate> ttlDelegate;
|
||||
|
||||
+ (nonnull instancetype)sharedInstance;
|
||||
|
||||
/// 获取指定账号对应<EFBFBD><EFBFBD>?HttpDnsService 实例
|
||||
/// 获取指定账号对应?HttpDnsService 实例
|
||||
/// @param accountID 账号 ID
|
||||
/// @return 已初始化的实例,若账号尚未注册则返回 nil
|
||||
+ (nullable instancetype)getInstanceByAccountId:(NSInteger)accountID;
|
||||
|
||||
/*!
|
||||
* @brief 无需鉴权功能的初始化接口
|
||||
* @details 初始化,设置 HTTPDNS 服务 Account ID。使用本接口初始化,请求将无任何签名保护,请谨慎使用<EFBFBD><EFBFBD>?
|
||||
* 您可以从控制台获取您<EFBFBD><EFBFBD>?Account ID <EFBFBD><EFBFBD>?
|
||||
* 此方法会初始化为单例<EFBFBD><EFBFBD>?
|
||||
* 注意:本接口<EFBFBD><EFBFBD>?.2.1起废弃,后续将进行移除<EFBFBD><EFBFBD>?
|
||||
* @details 初始化,设置 HTTPDNS 服务 Account ID。使用本接口初始化,请求将无任何签名保护,请谨慎使用?
|
||||
* 您可以从控制台获取您?Account ID ?
|
||||
* 此方法会初始化为单例?
|
||||
* 注意:本接口?.2.1起废弃,后续将进行移除?
|
||||
* @param accountID 您的 HTTPDNS Account ID
|
||||
*/
|
||||
- (nonnull instancetype)initWithAccountID:(NSInteger)accountID NEW_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 初始化、开启鉴权功能,并设<EFBFBD><EFBFBD>?HTTPDNS 服务 Account ID,鉴权功能对应的 secretKey<EFBFBD><EFBFBD>?
|
||||
* 您可以从控制台获取您<EFBFBD><EFBFBD>?Account ID 、secretKey信息<EFBFBD><EFBFBD>?
|
||||
* 此方法会初始化为单例<EFBFBD><EFBFBD>?
|
||||
* @details 初始化、开启鉴权功能,并设?HTTPDNS 服务 Account ID,鉴权功能对应的 secretKey?
|
||||
* 您可以从控制台获取您?Account ID 、secretKey信息?
|
||||
* 此方法会初始化为单例?
|
||||
* @param accountID 您的 HTTPDNS Account ID
|
||||
* @param secretKey 鉴权对应<EFBFBD><EFBFBD>?secretKey
|
||||
* @param secretKey 鉴权对应?secretKey
|
||||
*/
|
||||
- (nonnull instancetype)initWithAccountID:(NSInteger)accountID secretKey:(NSString * _Nonnull)secretKey;
|
||||
|
||||
/*!
|
||||
* @brief 启用鉴权功能、加密功能的初始化接<EFBFBD><EFBFBD>?
|
||||
* @details 初始化、开启鉴权功能、开启AES加密,并设置 HTTPDNS 服务 Account ID,鉴权功能对应的 secretKey,加密功能对应的 aesSecretKey<EFBFBD><EFBFBD>?
|
||||
* 您可以从控制台获取您<EFBFBD><EFBFBD>?Account ID 、secretKey、aesSecretKey 信息<EFBFBD><EFBFBD>?
|
||||
* 此方法会初始化为单例<EFBFBD><EFBFBD>?
|
||||
* @brief 启用鉴权功能、加密功能的初始化接?
|
||||
* @details 初始化、开启鉴权功能、开启AES加密,并设置 HTTPDNS 服务 Account ID,鉴权功能对应的 secretKey,加密功能对应的 aesSecretKey?
|
||||
* 您可以从控制台获取您?Account ID 、secretKey、aesSecretKey 信息?
|
||||
* 此方法会初始化为单例?
|
||||
* @param accountID 您的 HTTPDNS Account ID
|
||||
* @param secretKey 鉴权对应<EFBFBD><EFBFBD>?secretKey
|
||||
* @param aesSecretKey 加密功能对应<EFBFBD><EFBFBD>?aesSecretKey
|
||||
* @param secretKey 鉴权对应?secretKey
|
||||
* @param aesSecretKey 加密功能对应?aesSecretKey
|
||||
*/
|
||||
- (nonnull instancetype)initWithAccountID:(NSInteger)accountID secretKey:(NSString * _Nonnull)secretKey aesSecretKey:(NSString * _Nullable)aesSecretKey;
|
||||
|
||||
|
||||
/// 开启鉴权功能后,鉴权的签名计算默认读取设备当前时间。若担心设备时间不准确导致签名不准确,可以使用此接口校正 APP 内鉴权计算使用的时间<EFBFBD><EFBFBD>?
|
||||
/// 注意,校正操作在 APP 的一个生命周期内生效,APP 重启后需要重新设置才能重新生<EFBFBD><EFBFBD>?
|
||||
/// @param authCurrentTime 用于校正的时间戳,单位为<EFBFBD><EFBFBD>?
|
||||
- (void)setAuthCurrentTime:(NSUInteger)authCurrentTime NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setInternalAuthTimeBaseBySpecifyingCurrentTime:] instead.");
|
||||
/// 开启鉴权功能后,鉴权的签名计算默认读取设备当前时间。若担心设备时间不准确导致签名不准确,可以使用此接口校正 APP 内鉴权计算使用的时间?
|
||||
/// 注意,校正操作在 APP 的一个生命周期内生效,APP 重启后需要重新设置才能重新生?
|
||||
/// @param authCurrentTime 用于校正的时间戳,单位为?
|
||||
- (void)setAuthCurrentTime:(NSUInteger)authCurrentTime Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setInternalAuthTimeBaseBySpecifyingCurrentTime:] instead.");
|
||||
|
||||
|
||||
/// 开启鉴权功能后,鉴权的签名计算默认读取设备当前时间。若担心设备时间不准确导致签名不准确,可以使用此接口校正 APP 内鉴权计算使用的时间<EFBFBD><EFBFBD>?
|
||||
/// 注意,校正操作在 APP 的一个生命周期内生效,APP 重启后需要重新设置才能重新生<EFBFBD><EFBFBD>?
|
||||
/// @param currentTime 用于校正的时间戳,单位为<EFBFBD><EFBFBD>?
|
||||
/// 开启鉴权功能后,鉴权的签名计算默认读取设备当前时间。若担心设备时间不准确导致签名不准确,可以使用此接口校正 APP 内鉴权计算使用的时间?
|
||||
/// 注意,校正操作在 APP 的一个生命周期内生效,APP 重启后需要重新设置才能重新生?
|
||||
/// @param currentTime 用于校正的时间戳,单位为?
|
||||
- (void)setInternalAuthTimeBaseBySpecifyingCurrentTime:(NSTimeInterval)currentTime;
|
||||
|
||||
|
||||
/// 设置持久化缓存功<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setCachedIPEnabled:(BOOL)enable NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setPersistentCacheIPEnabled:] instead.");
|
||||
/// 设置持久化缓存功?
|
||||
/// @param enable YES: 开?NO: 关闭
|
||||
- (void)setCachedIPEnabled:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setPersistentCacheIPEnabled:] instead.");
|
||||
|
||||
|
||||
/// 设置持久化缓存功<EFBFBD><EFBFBD>?
|
||||
/// 设置持久化缓存功?
|
||||
/// 开启后,每次解析会将结果持久化缓存到本地,当下次应用启动时,可以从本地加载缓存解析结果,提高应用启动时获取解析结果的速度
|
||||
/// 加载时,会丢弃已经过期的解析结果
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
/// @param enable YES: 开?NO: 关闭
|
||||
- (void)setPersistentCacheIPEnabled:(BOOL)enable;
|
||||
|
||||
/// 设置持久化缓存功<EFBFBD><EFBFBD>?
|
||||
/// 设置持久化缓存功?
|
||||
/// 开启后,每次解析会将结果持久化缓存到本地,当下次应用启动时,可以从本地加载缓存解析结果,提高应用启动时获取解析结果的速度
|
||||
/// 加载时,会丢弃过期时间已经超过指定值的解析结果
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
/// @param duration 决定丢弃IP的过期时间阈值,单位为秒,过期超过这个时间范围的IP会被丢弃,取值范围为0-1年。这个值仅在开启持久化缓存功能时才有意<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: 开?NO: 关闭
|
||||
/// @param duration 决定丢弃IP的过期时间阈值,单位为秒,过期超过这个时间范围的IP会被丢弃,取值范围为0-1年。这个值仅在开启持久化缓存功能时才有意?
|
||||
- (void)setPersistentCacheIPEnabled:(BOOL)enable discardRecordsHasExpiredFor:(NSTimeInterval)duration;
|
||||
|
||||
/// 是否允许 HTTPDNS 返回 TTL 过期域名<EFBFBD><EFBFBD>?ip ,建议允许(默认不允许)
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setExpiredIPEnabled:(BOOL)enable NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setReuseExpiredIPEnabled:] instead.");
|
||||
/// 是否允许 HTTPDNS 返回 TTL 过期域名?ip ,建议允许(默认不允许)
|
||||
/// @param enable YES: 开?NO: 关闭
|
||||
- (void)setExpiredIPEnabled:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setReuseExpiredIPEnabled:] instead.");
|
||||
|
||||
|
||||
/// 是否允许 HTTPDNS 返回 TTL 过期域名<EFBFBD><EFBFBD>?ip ,建议允许(默认不允许)
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
/// 是否允许 HTTPDNS 返回 TTL 过期域名?ip ,建议允许(默认不允许)
|
||||
/// @param enable YES: 开?NO: 关闭
|
||||
- (void)setReuseExpiredIPEnabled:(BOOL)enable;
|
||||
|
||||
|
||||
/// 设置 HTTPDNS 域名解析请求类型 ( HTTP / HTTPS )
|
||||
/// 若不调用该接口,默认<EFBFBD><EFBFBD>?HTTP 请求<EFBFBD><EFBFBD>?
|
||||
/// HTTP 请求基于底层 CFNetwork 实现,不<EFBFBD><EFBFBD>?ATS 限制<EFBFBD><EFBFBD>?
|
||||
/// 若不调用该接口,默认?HTTP 请求?
|
||||
/// HTTP 请求基于底层 CFNetwork 实现,不?ATS 限制?
|
||||
/// @param enable YES: HTTPS请求 NO: HTTP请求
|
||||
- (void)setHTTPSRequestEnabled:(BOOL)enable;
|
||||
|
||||
|
||||
/// 声明App是否配置了ATS为AllowsArbitraryLoads,默认认为没有配<EFBFBD><EFBFBD>?
|
||||
/// 声明App是否配置了ATS为AllowsArbitraryLoads,默认认为没有配?
|
||||
/// 若做了声明,则当指定走HTTP方式解析域名时,解析链路会走系统NSURLSession逻辑
|
||||
/// 否则,会走定制的CFHTTP链路,避免被ATS拦截请求
|
||||
- (void)setHasAllowedArbitraryLoadsInATS:(BOOL)hasAllowedArbitraryLoadsInATS;
|
||||
|
||||
|
||||
/// 设置底层HTTPDNS网络请求超时时间,单位为<EFBFBD><EFBFBD>?
|
||||
/// 需要注意,这个值只决定底层解析请求的网络超时时间,而非同步解析接口、异步解析接口的最长阻塞或者等待时<EFBFBD><EFBFBD>?
|
||||
/// 设置底层HTTPDNS网络请求超时时间,单位为?
|
||||
/// 需要注意,这个值只决定底层解析请求的网络超时时间,而非同步解析接口、异步解析接口的最长阻塞或者等待时?
|
||||
/// 同步解析接口、异步解析接口的最长阻塞或者等待时间,需要调用接口时设置request参数中的`resolveTimeoutInSecond`决定
|
||||
/// @param timeoutInterval 超时时间,单位为<EFBFBD><EFBFBD>?
|
||||
/// @param timeoutInterval 超时时间,单位为?
|
||||
- (void)setNetworkingTimeoutInterval:(NSTimeInterval)timeoutInterval;
|
||||
|
||||
|
||||
/// 指定region,指定后会读取该region对应配置作为初始化配<EFBFBD><EFBFBD>?
|
||||
/// 一般情况下无需设置,SDK内部会默认路由全球范围内最近的接入<EFBFBD><EFBFBD>?
|
||||
/// 指定region,指定后会读取该region对应配置作为初始化配?
|
||||
/// 一般情况下无需设置,SDK内部会默认路由全球范围内最近的接入?
|
||||
/// @param region 需要指定的region,缺省为中国大陆
|
||||
- (void)setRegion:(NSString *)region;
|
||||
|
||||
|
||||
/// 域名预解<EFBFBD><EFBFBD>?(默认解析双栈记录)
|
||||
/// 通常用于启动后立即向SDK设置您后续可能会使用到的热点域名,以便SDK提前解析,减少后续解析域名时请求的时<EFBFBD><EFBFBD>?
|
||||
/// 如果是在运行过程中调用,SDK也会立即解析设置的域名数组中的域名,刷新这些域名的解析结<EFBFBD><EFBFBD>?
|
||||
/// 域名预解?(默认解析双栈记录)
|
||||
/// 通常用于启动后立即向SDK设置您后续可能会使用到的热点域名,以便SDK提前解析,减少后续解析域名时请求的时?
|
||||
/// 如果是在运行过程中调用,SDK也会立即解析设置的域名数组中的域名,刷新这些域名的解析结?
|
||||
///
|
||||
/// @param hosts 预解析列表数<EFBFBD><EFBFBD>?
|
||||
/// @param hosts 预解析列表数?
|
||||
- (void)setPreResolveHosts:(NSArray *)hosts;
|
||||
|
||||
|
||||
/// 域名预解析,可以指定预解析auto、ipv4、ipv6、both
|
||||
/// 通常用于启动后立即向SDK设置您后续可能会使用到的热点域名,以便SDK提前解析,减少后续解析域名时请求的时<EFBFBD><EFBFBD>?
|
||||
/// 如果是在运行过程中调用,SDK也会立即解析设置的域名数组中的域名,刷新这些域名的解析结<EFBFBD><EFBFBD>?
|
||||
/// 通常用于启动后立即向SDK设置您后续可能会使用到的热点域名,以便SDK提前解析,减少后续解析域名时请求的时?
|
||||
/// 如果是在运行过程中调用,SDK也会立即解析设置的域名数组中的域名,刷新这些域名的解析结?
|
||||
///
|
||||
/// @param hosts 预解析列表数<EFBFBD><EFBFBD>?
|
||||
/// @param ipType 指定预解析记录类<EFBFBD><EFBFBD>?
|
||||
/// @param hosts 预解析列表数?
|
||||
/// @param ipType 指定预解析记录类?
|
||||
- (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:(NewHttpDNS_IPType)ipType NEW_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 开<EFBFBD><EFBFBD>?
|
||||
/// 本地日志 log 开?
|
||||
/// @param enable YES: 打开 NO: 关闭
|
||||
- (void)setLogEnabled:(BOOL)enable;
|
||||
|
||||
|
||||
/// 设置网络切换时是否自动更新所有域名解析结<EFBFBD><EFBFBD>?
|
||||
/// 如果打开此开关,在网络切换时,会自动刷新所有域名的解析结果,但会产生一定流量消<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
/// 设置网络切换时是否自动更新所有域名解析结?
|
||||
/// 如果打开此开关,在网络切换时,会自动刷新所有域名的解析结果,但会产生一定流量消?
|
||||
/// @param enable YES: 开?NO: 关闭
|
||||
- (void)setPreResolveAfterNetworkChanged:(BOOL)enable;
|
||||
|
||||
|
||||
/// 设置当httpdns解析失败时是否降级到localDNS尝试解析
|
||||
/// 降级生效时,SDNS参数不生效,降级逻辑只解析域名,返回的结果默认使<EFBFBD><EFBFBD>?0<><30>?若未指定该域名自定义TTL)作为TTL<EFBFBD><EFBFBD>?
|
||||
/// 降级生效时,SDNS参数不生效,降级逻辑只解析域名,返回的结果默认使?0?若未指定该域名自定义TTL)作为TTL?
|
||||
/// 降级请求也不会再对ip进行优先排序
|
||||
/// 默认关闭,不会自动降<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES:自动降<EFBFBD><EFBFBD>?NO:不自动降级
|
||||
/// 默认关闭,不会自动降?
|
||||
/// @param enable YES:自动降?NO:不自动降级
|
||||
- (void)setDegradeToLocalDNSEnabled:(BOOL)enable;
|
||||
|
||||
|
||||
@@ -219,59 +219,59 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)setIPRankingDatasource:(NSDictionary<NSString *, NSNumber *> *)IPRankingDatasource;
|
||||
|
||||
|
||||
/// 设置是否 开<EFBFBD><EFBFBD>?IPv6 结果解析。只有开启状态下,对域名的解析才会尝试解析v6记录并返回v6的结<EFBFBD><EFBFBD>?
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)enableIPv6:(BOOL)enable NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setIPv6Enabled:] instead.");
|
||||
/// 设置是否 开?IPv6 结果解析。只有开启状态下,对域名的解析才会尝试解析v6记录并返回v6的结?
|
||||
/// @param enable YES: 开?NO: 关闭
|
||||
- (void)enableIPv6:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService setIPv6Enabled:] instead.");
|
||||
|
||||
|
||||
/// 设置是否 开<EFBFBD><EFBFBD>?IPv6 结果解析。只有开启状态下,对域名的解析才会尝试解析v6记录并返回v6的结<EFBFBD><EFBFBD>?
|
||||
/// 设置是否 开?IPv6 结果解析。只有开启状态下,对域名的解析才会尝试解析v6记录并返回v6的结?
|
||||
/// 已弃用。默认支持IPv6。如果不需要IPv6类型的结果,只需在请求时指定`queryIpType`为`HttpdnsQueryIPTypeIpv4`
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭
|
||||
- (void)setIPv6Enabled:(BOOL)enable NEW_HTTPDNS_DEPRECATED("Deprecated. If ipv6 is unnecessary, you can set the `queryIpType` as HttpdnsQueryIPTypeIpv4 when resolving domain.");
|
||||
/// @param enable YES: 开?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: 开<EFBFBD><EFBFBD>?NO: 关闭 ,默认关<EFBFBD><EFBFBD>?
|
||||
- (void)enableNetworkInfo:(BOOL)enable NEW_HTTPDNS_DEPRECATED("Deprecated. We do not utilize network information anymore");
|
||||
/// @param enable YES: 开?NO: 关闭 ,默认关?
|
||||
- (void)enableNetworkInfo:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. We do not utilize network information anymore");
|
||||
|
||||
|
||||
/// 是否允许通过 CNCopyCurrentNetworkInfo 获取wifi ssid bssid
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭 ,默认关<EFBFBD><EFBFBD>?
|
||||
- (void)setReadNetworkInfoEnabled:(BOOL)enable NEW_HTTPDNS_DEPRECATED("Deprecated. We do not utilize network information anymore.");
|
||||
/// @param enable YES: 开?NO: 关闭 ,默认关?
|
||||
- (void)setReadNetworkInfoEnabled:(BOOL)enable Trust_HTTPDNS_DEPRECATED("Deprecated. We do not utilize network information anymore.");
|
||||
|
||||
|
||||
/// 是否开启IP探测功能
|
||||
/// @param enable YES: 开<EFBFBD><EFBFBD>?NO: 关闭 默认打开
|
||||
- (void)enableCustomIPRank:(BOOL)enable NEW_HTTPDNS_DEPRECATED("Deprecated, will be removed in the future.");
|
||||
/// @param enable YES: 开?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;
|
||||
|
||||
|
||||
/// 获取用于用户追踪<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
|
||||
/// 获取用于用户追踪?sessionId
|
||||
/// sessionId为随机生成,长度?12 位,App 生命周期内保持不?
|
||||
/// 为了排查可能的解析问题,需要您?sessionId 和解析出?IP 一起记录在日志?
|
||||
/// 请参? 解析异常排查?“会话追踪方案?https://help.TrustAPP.com/document_detail/100530.html
|
||||
- (NSString *)getSessionId;
|
||||
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结<EFBFBD><EFBFBD>?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑<EFBFBD><EFBFBD>?
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑?
|
||||
/// @param host 需要解析的域名
|
||||
/// @param queryIpType 可设置为自动选择,ipv4,ipv6. 设置为自动选择时,会自动根据当前所处网络环境选择解析ipv4或ipv6
|
||||
/// @return 解析结果
|
||||
- (nullable HttpdnsResult *)resolveHostSync:(NSString *)host byIpType:(HttpdnsQueryIPType)queryIpType;
|
||||
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结<EFBFBD><EFBFBD>?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑<EFBFBD><EFBFBD>?
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑?
|
||||
/// @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;
|
||||
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结<EFBFBD><EFBFBD>?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结<EFBFBD><EFBFBD>?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑<EFBFBD><EFBFBD>?
|
||||
/// 同步解析域名,会阻塞当前线程,直到从缓存中获取到有效解析结果,或者从服务器拿到最新解析结?
|
||||
/// 如果允许复用过期的解析结果且存在过期结果的情况下,会先返回这个结果,然后启动后台线程去更新解析结?
|
||||
/// 为了防止在主线程中误用本接口导致APP卡顿,本接口会做检测,若发现调用线程是主线程,则自动降级到resolveHostSyncNonBlocking接口的实现逻辑?
|
||||
/// @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 NEW_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 NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSArray *)getIpsByHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6, 单IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6, 单IP (需要开启ipv6 开?enableIPv6?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请?
|
||||
/// @param host 域名
|
||||
- (NSString *)getIPv6ByHostAsync:(NSString *)host NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSString *)getIPv6ByHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6数组, 多IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6数组, 多IP (需要开启ipv6 开?enableIPv6?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请?
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv6sByHostAsync:(NSString *)host NEW_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 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 同时获取ipv4 ipv6的IP (需要开启ipv6 开?enableIPv6?
|
||||
/// @param host 域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// NewHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// NewHDNS_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 NEW_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地址<EFBFBD><EFBFBD>?
|
||||
/// 使用此API 需要确<EFBFBD><EFBFBD>?enableIPv6 开关已打开
|
||||
/// 根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址?
|
||||
/// 使用此API 需要确?enableIPv6 开关已打开
|
||||
/// 设备网络 返回域名IP
|
||||
/// IPv4 Only IPv4
|
||||
/// IPv6 Only IPv6 (如果没有Pv6返回空)
|
||||
@@ -368,75 +368,75 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @param host 要解析的域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// NewHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// NewHDNS_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 NEW_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 NEW_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 NEW_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, 但是超时上限<EFBFBD><EFBFBD>?s<><73>?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大<EFBFBD><EFBFBD>?s,同步接口也最多阻塞当前线<EFBFBD><EFBFBD>?s
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起同步解析请<EFBFBD><EFBFBD>?
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限?s?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大?s,同步接口也最多阻塞当前线?s
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起同步解析请?
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv4ListForHostSync:(NSString *)host NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
- (NSArray *)getIPv4ListForHostSync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6, 单IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6, 单IP (需要开启ipv6 开?enableIPv6?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请?
|
||||
/// @param host 域名
|
||||
- (NSString *)getIPv6ForHostAsync:(NSString *)host NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSString *)getIPv6ForHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6数组, 多IP (需要开启ipv6 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// 异步接口,首次结果可能为空,获取域名对应的ipv6数组, 多IP (需要开启ipv6 开?enableIPv6?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请?
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv6ListForHostAsync:(NSString *)host NEW_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, 但是超时上限<EFBFBD><EFBFBD>?s<><73>?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大<EFBFBD><EFBFBD>?s,同步接口也最多阻塞当前线<EFBFBD><EFBFBD>?s
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限?s?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大?s,同步接口也最多阻塞当前线?s
|
||||
/// @param host 域名
|
||||
- (NSArray *)getIPv6ListForHostSync:(NSString *)host NEW_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 NEW_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 开<EFBFBD><EFBFBD>?enableIPv6<EFBFBD><EFBFBD>?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请<EFBFBD><EFBFBD>?
|
||||
/// 异步接口,首次结果可能为空,同时获取ipv4 ipv6的IP (需要开启ipv6 开?enableIPv6?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请?
|
||||
/// @param host 域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// NewHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// NewHDNS_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 NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
- (NSDictionary <NSString *, NSArray *>*)getHttpDnsResultHostAsync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:] instead.");
|
||||
|
||||
/// 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>?
|
||||
/// NOTE: 同步接口,必须在子线程中执行,否则会转变为异步接?
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限?s?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大?s,同步接口也最多阻塞当前线?s
|
||||
/// 同时获取ipv4 + ipv6的IP (需要开启ipv6 开?enableIPv6?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请?
|
||||
/// @param host 域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// NewHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// NewHDNS_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 NEW_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地址<EFBFBD><EFBFBD>?
|
||||
/// 使用此API 需要确<EFBFBD><EFBFBD>?enableIPv6 开关已打开
|
||||
/// 异步接口,首次结果可能为空,根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址?
|
||||
/// 使用此API 需要确?enableIPv6 开关已打开
|
||||
/// 设备网络 返回域名IP
|
||||
/// IPv4 Only IPv4
|
||||
/// IPv6 Only IPv6 (如果没有Pv6返回空)
|
||||
@@ -444,33 +444,33 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/// @param host 要解析的域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// NewHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// NewHDNS_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 NEW_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地址组,同步接口,必须在子线程中执行,否则会转变为异步接<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>?
|
||||
/// 根据当前设备的网络状态自动返回域名对应的 IPv4/IPv6地址组,同步接口,必须在子线程中执行,否则会转变为异步接?
|
||||
/// 同步接口有超时机制,超时时间为[HttpDnsService sharedInstance].timeoutInterval, 但是超时上限?s?
|
||||
/// 即使[HttpDnsService sharedInstance].timeoutInterval设置的时间大?s,同步接口也最多阻塞当前线?s
|
||||
/// 根据当前网络栈自动获取ipv4 ipv6的IP (需要开启ipv6 开?enableIPv6?
|
||||
/// 先查询缓存,缓存中存在未过期的结果,则直接返回结果,如果缓存未命中,则发起异步解析请?
|
||||
/// @param host 域名
|
||||
/// @result 返回字典类型结构
|
||||
/// {
|
||||
/// NewHDNS_IPV4: ['xxx.xxx.xxx.xxx', 'xxx.xxx.xxx.xxx'],
|
||||
/// NewHDNS_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 NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
- (NSDictionary <NSString *, NSArray *>*)autoGetHttpDnsResultForHostSync:(NSString *)host Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSync:byIpType:] instead.");
|
||||
|
||||
/// 软件自定义解析接<EFBFBD><EFBFBD>?
|
||||
- (NSDictionary *)getIpsByHostAsync:(NSString *)host withParams:(NSDictionary<NSString *, NSString *> *)params withCacheKey:(NSString *)cacheKey NEW_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:withSdnsParams:sdnsCacheKey:] instead.");
|
||||
/// 软件自定义解析接?
|
||||
- (NSDictionary *)getIpsByHostAsync:(NSString *)host withParams:(NSDictionary<NSString *, NSString *> *)params withCacheKey:(NSString *)cacheKey Trust_HTTPDNS_DEPRECATED("Deprecated. Use -[HttpDnsService resolveHostSyncNonBlocking:byIpType:withSdnsParams:sdnsCacheKey:] instead.");
|
||||
|
||||
|
||||
/// 清除指定host缓存(内<EFBFBD><EFBFBD>?沙盒数据库)
|
||||
/// 清除指定host缓存(内?沙盒数据库)
|
||||
/// @param hostArray 需要清除的host域名数组。如果需要清空全部数据传nil或者空数组即可
|
||||
- (void)cleanHostCache:(nullable NSArray<NSString *> *)hostArray;
|
||||
|
||||
/// 清除当前所有host缓存 (内存+沙盒数据<EFBFBD><EFBFBD>?
|
||||
/// 清除当前所有host缓存 (内存+沙盒数据?
|
||||
- (void)cleanAllHostCache;
|
||||
|
||||
/// 清理已经配置的软件自定义解析全局参数
|
||||
|
||||
21
HttpDNSSDK/sdk/ios/NewHttpDNS/HttpdnsService_utf8.h
Normal file
21
HttpDNSSDK/sdk/ios/NewHttpDNS/HttpdnsService_utf8.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
// 澶存枃浠跺寘鍚渶浣跨敤鐩稿鐩綍锛岀‘淇濋
|
||||
@@ -1,24 +1,30 @@
|
||||
//
|
||||
// HttpdnsRequest.h
|
||||
// NewHttpDNS
|
||||
// TrustHttpDNS
|
||||
//
|
||||
// Created by xuyecan on 2024/5/19.
|
||||
// Copyright © 2024 trustapp.com. All rights reserved.
|
||||
// Copyright ツゥ 2024 trustapp.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#ifndef NewHTTPDNSQUERYIPTYPE
|
||||
#define NewHTTPDNSQUERYIPTYPE
|
||||
#ifndef NEWHTTPDNSQUERYIPTYPE
|
||||
#define NEWHTTPDNSQUERYIPTYPE
|
||||
|
||||
typedef enum {
|
||||
NewHttpDNS_IPTypeV4 = 0, //ipv4
|
||||
NewHttpDNS_IPTypeV6 = 1, //ipv6
|
||||
NewHttpDNS_IPTypeV64 = 2, //ipv4 + ipv6
|
||||
NewHttpDNS_IPTypeDual = 2, //both
|
||||
} NewHttpDNS_IPType;
|
||||
|
||||
// Compatibility aliases
|
||||
typedef NewHttpDNS_IPType TrustHttpDNS_IPType;
|
||||
#define TrustHttpDNS_IPTypeV4 NewHttpDNS_IPTypeV4
|
||||
#define TrustHttpDNS_IPTypeV6 NewHttpDNS_IPTypeV6
|
||||
#define TrustHttpDNS_IPTypeV64 NewHttpDNS_IPTypeDual
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, HttpdnsQueryIPType) {
|
||||
HttpdnsQueryIPTypeAuto NS_SWIFT_NAME(auto) = 0,
|
||||
HttpdnsQueryIPTypeIpv4 = 1 << 0,
|
||||
@@ -30,27 +36,27 @@ typedef NS_OPTIONS(NSUInteger, HttpdnsQueryIPType) {
|
||||
|
||||
@interface HttpdnsRequest : NSObject
|
||||
|
||||
/// 需要解析的域名
|
||||
/// 髴隕∬ァ」譫千噪蝓溷錐
|
||||
@property (nonatomic, copy) NSString *host;
|
||||
|
||||
/// 解析超时时间,对于同步接口,即为最大等待时间,对于异步接口,即为最大等待回调时<EFBFBD><EFBFBD>?
|
||||
/// 默认<EFBFBD><EFBFBD>?秒,取值必须在0.5<EFBFBD><EFBFBD>?- 5秒之<EFBFBD><EFBFBD>?
|
||||
/// 隗」譫占カ<EFBFBD>慮譌カ髣エ<EFBFBD>悟ッケ莠主酔豁・謗・蜿」<EFBFBD>悟叉荳コ譛螟ァ遲牙セ<EFBFBD>慮髣エ<EFBFBD>悟ッケ莠主シよュ・謗・蜿」<EFBFBD>悟叉荳コ譛螟ァ遲牙セ<EFBFBD>屓隹<EFBFBD>慮髣?
|
||||
/// 鮟倩ョ、蛟?遘抵シ悟叙蛟シ蠢<EFBDBC>。サ蝨ィ0.5遘?- 5遘剃ケ矩<EFBFBD>?
|
||||
@property (nonatomic, assign) double resolveTimeoutInSecond;
|
||||
|
||||
/// 查询IP类型
|
||||
/// 默认为HttpdnsQueryIPTypeAuto,此类型下,SDK至少会请求解析ipv4地址,若判断到当前网络环境支持ipv6,则还会请求解析ipv6地址
|
||||
/// HttpdnsQueryIPTypeIpv4,只请求解析ipv4
|
||||
/// HttpdnsQueryIPTypeIpv6,只请求解析ipv6
|
||||
/// HttpdnsQueryIPTypeBoth,不管当前网络环境是什么,会尝试同时请求解析ipv4地址和ipv6地址,这种用法,通常需要拿到结果之后自行判断网络环境决定使用哪个结<EFBFBD><EFBFBD>?
|
||||
/// 譟・隸「IP邀サ蝙<EFBFBD>
|
||||
/// 鮟倩ョ、荳コHttpdnsQueryIPTypeAuto<EFBFBD>梧ュ、邀サ蝙倶ク具シ郡DK閾ウ蟆台シ夊ッキ豎りァ」譫進pv4蝨ー蝮<EFBFBD>瑚凶蛻、譁ュ蛻ー蠖灘燕鄂醍サ懃識蠅<EFBFBD>髪謖(pv6<EFBFBD>悟<EFBFBD>霑倅シ夊ッキ豎りァ」譫進pv6蝨ー蝮
|
||||
/// HttpdnsQueryIPTypeIpv4<EFBFBD>悟宵隸キ豎りァ」譫進pv4
|
||||
/// HttpdnsQueryIPTypeIpv6<EFBFBD>悟宵隸キ豎りァ」譫進pv6
|
||||
/// HttpdnsQueryIPTypeBoth<EFBFBD>御ク咲ョ。蠖灘燕鄂醍サ懃識蠅<EFBFBD>弍莉荵茨シ御シ壼ー晁ッ募酔譌カ隸キ豎りァ」譫進pv4蝨ー蝮蜥景pv6蝨ー蝮<EFBFBD>瑚ソ咏ァ咲畑豕包シ碁壼クク髴隕∵響蛻ー扈捺棡荵句錘閾ェ陦悟愛譁ュ鄂醍サ懃識蠅<EFBFBD><EFBFBD>螳壻スソ逕ィ蜩ェ荳ェ扈捺<EFBFBD>?
|
||||
@property (nonatomic, assign) HttpdnsQueryIPType queryIpType;
|
||||
|
||||
/// SDNS参数,针对软件自定义解析场景使用
|
||||
/// SDNS蜿よ焚<EFBFBD>碁宙蟇ケ霓ッ莉カ閾ェ螳壻ケ芽ァ」譫仙惻譎ッ菴ソ逕ィ
|
||||
@property (nonatomic, copy, nullable) NSDictionary<NSString *, NSString *> *sdnsParams;
|
||||
|
||||
/// 缓存Key,针对软件自定义解析场景使用
|
||||
/// 郛灘ュ婁ey<EFBFBD>碁宙蟇ケ霓ッ莉カ閾ェ螳壻ケ芽ァ」譫仙惻譎ッ菴ソ逕ィ
|
||||
@property (nonatomic, copy, nullable) NSString *cacheKey;
|
||||
|
||||
/// 请求所属的账号ID,用于在多账号场景下定位实例
|
||||
/// 隸キ豎よ園螻樒噪雍ヲ蜿キID<EFBFBD>檎畑莠主惠螟夊エヲ蜿キ蝨コ譎ッ荳句ョ壻ス榊ョ樔セ<EFBFBD>
|
||||
@property (nonatomic, assign) NSInteger accountId;
|
||||
|
||||
- (instancetype)initWithHost:(NSString *)host queryIpType:(HttpdnsQueryIPType)queryIpType;
|
||||
|
||||
@@ -37,8 +37,8 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
|
||||
@interface HttpdnsScheduleCenter ()
|
||||
|
||||
// 为了简单,无论v4还是v6,都只共同维<EFBFBD><EFBFBD>?个下<EFBFBD><EFBFBD>?
|
||||
// 一般而言,下标当前在哪里并不是那么重要,重要的是轮询的能<EFBFBD><EFBFBD>?
|
||||
// 为了简单,无论v4还是v6,都只共同维?个下?
|
||||
// 一般而言,下标当前在哪里并不是那么重要,重要的是轮询的能?
|
||||
@property (nonatomic, assign) int currentActiveServiceHostIndex;
|
||||
@property (nonatomic, assign) int currentActiveUpdateHostIndex;
|
||||
|
||||
@@ -85,7 +85,7 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
_scheduleCenterResultPath = [[HttpdnsPersistenceUtils scheduleCenterResultDirectory]
|
||||
stringByAppendingPathComponent:kScheduleRegionConfigLocalCacheFileName];
|
||||
|
||||
// 上次更新日期默认设置<EFBFBD><EFBFBD>?天前,这样如果缓存没有记录,就会立即更新
|
||||
// 上次更新日期默认设置?天前,这样如果缓存没有记录,就会立即更新
|
||||
_lastScheduleCenterConnectDate = [NSDate dateWithTimeIntervalSinceNow:(- 24 * 60 * 60)];
|
||||
}
|
||||
return self;
|
||||
@@ -93,11 +93,11 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
|
||||
- (void)initRegion:(NSString *)region {
|
||||
if (![[HttpdnsRegionConfigLoader getAvailableRegionList] containsObject:region]) {
|
||||
region = NEW_HTTPDNS_DEFAULT_REGION_KEY;
|
||||
region = Trust_HTTPDNS_DEFAULT_REGION_KEY;
|
||||
}
|
||||
|
||||
// 先用默认region初始<EFBFBD><EFBFBD>?
|
||||
// 如果用户主动调用了设置region接口,会按照用户设置的再来一<EFBFBD><EFBFBD>?
|
||||
// 先用默认region初始?
|
||||
// 如果用户主动调用了设置region接口,会按照用户设置的再来一?
|
||||
[self initServerListByRegion:region];
|
||||
|
||||
// 再从本地缓存读取之前缓存过的配置
|
||||
@@ -112,7 +112,7 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
self.currentActiveUpdateHostIndex = 0;
|
||||
});
|
||||
|
||||
// 重置region之后马上发起一次更<EFBFBD><EFBFBD>?
|
||||
// 重置region之后马上发起一次更?
|
||||
[self asyncUpdateRegionScheduleConfig];
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
}
|
||||
NSDictionary *scheduleCenterResult = (NSDictionary *)obj;
|
||||
|
||||
// 兼容时间戳为NSNumber/NSString,屏蔽NSNull等异常输<EFBFBD><EFBFBD>?
|
||||
// 兼容时间戳为NSNumber/NSString,屏蔽NSNull等异常输?
|
||||
id ts = [scheduleCenterResult objectForKey:kLastUpdateUnixTimestampKey];
|
||||
if ([ts respondsToSelector:@selector(doubleValue)]) {
|
||||
NSDate *lastUpdateDate = [NSDate dateWithTimeIntervalSince1970:[ts doubleValue]];
|
||||
@@ -135,7 +135,7 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
});
|
||||
}
|
||||
|
||||
// 根据指定的时间间隔检查是否需要更<EFBFBD><EFBFBD>?
|
||||
// 根据指定的时间间隔检查是否需要更?
|
||||
- (void)asyncUpdateRegionConfigAfterAtLeast:(NSTimeInterval)interval {
|
||||
__block BOOL shouldUpdate = NO;
|
||||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||||
@@ -170,10 +170,10 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
if (error || !scheduleCenterResult) {
|
||||
HttpdnsLogDebug("Update region config failed, error: %@", error);
|
||||
|
||||
// 只有报错了就尝试选择新的调度服务<EFBFBD><EFBFBD>?
|
||||
// 只有报错了就尝试选择新的调度服务?
|
||||
[self rotateUpdateServerHost];
|
||||
|
||||
// 3秒之后重<EFBFBD><EFBFBD>?
|
||||
// 3秒之后重?
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((retryCount + 1) * NSEC_PER_SEC)), self->_scheduleFetchConfigAsyncQueue, ^{
|
||||
[self asyncUpdateRegionScheduleConfigAtRetry:retryCount + 1];
|
||||
});
|
||||
@@ -192,8 +192,8 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
}
|
||||
|
||||
- (void)updateRegionConfig:(NSDictionary *)scheduleCenterResult {
|
||||
NSArray *v4Result = [scheduleCenterResult objectForKey:kNewHttpdnsRegionConfigV4HostKey];
|
||||
NSArray *v6Result = [scheduleCenterResult objectForKey:kNewHttpdnsRegionConfigV6HostKey];
|
||||
NSArray *v4Result = [scheduleCenterResult objectForKey:kTrustHttpdnsRegionConfigV4HostKey];
|
||||
NSArray *v6Result = [scheduleCenterResult objectForKey:kTrustHttpdnsRegionConfigV6HostKey];
|
||||
|
||||
dispatch_sync(_scheduleConfigLocalOperationQueue, ^{
|
||||
HttpdnsRegionConfigLoader *regionConfigLoader = [HttpdnsRegionConfigLoader sharedInstance];
|
||||
@@ -260,7 +260,7 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
});
|
||||
|
||||
if (timeToUpdate) {
|
||||
// 每次服务server列表轮转之后,尝<EFBFBD><EFBFBD>?个至少间<EFBFBD><EFBFBD>?0秒的更新
|
||||
// 每次服务server列表轮转之后,尝?个至少间?0秒的更新
|
||||
[self asyncUpdateRegionConfigAfterAtLeast:30];
|
||||
}
|
||||
}
|
||||
@@ -286,8 +286,8 @@ static int const MAX_UPDATE_RETRY_COUNT = 2;
|
||||
}
|
||||
|
||||
- (NSString *)currentActiveServiceServerV4Host {
|
||||
// 每次读取时都检查是否需要更新,相当于实现一个懒加载的机<EFBFBD><EFBFBD>?
|
||||
// 因为当前httpdns的初始化方式,没有一个统一的初始化入口,所以需要这样处<EFBFBD><EFBFBD>?
|
||||
// 每次读取时都检查是否需要更新,相当于实现一个懒加载的机?
|
||||
// 因为当前httpdns的初始化方式,没有一个统一的初始化入口,所以需要这样处?
|
||||
[self asyncUpdateRegionConfigAfterAtLeast:(24 * 60 * 60)];
|
||||
|
||||
// 检查是否存在HTTPDNS_DEBUG_V4_SERVICE_IP环境变量
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
<dict>
|
||||
<key>appId</key>
|
||||
<string>app1flndpo9</string>
|
||||
<key>primaryServiceHost</key>
|
||||
<string>httpdns.deepwaf.xyz</string>
|
||||
<key>servicePort</key>
|
||||
<integer>8445</integer>
|
||||
<key>apiUrl</key>
|
||||
<string>https://httpdns.deepwaf.xyz:8445</string>
|
||||
<key>signSecret</key>
|
||||
<string></string>
|
||||
<string>ss_67fb8471a45b</string>
|
||||
<key>serviceDomain</key>
|
||||
<string>demo.cloudxdr.com</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//
|
||||
// DemoConfigLoader.h
|
||||
// NewHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -8,10 +10,15 @@
|
||||
@interface DemoConfigLoader : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *appId;
|
||||
@property (nonatomic, copy, readonly) NSString *primaryServiceHost;
|
||||
@property (nonatomic, assign, readonly) NSInteger servicePort;
|
||||
@property (nonatomic, copy, readonly, nullable) NSString *signSecret;
|
||||
@property (nonatomic, copy, readonly) NSString *apiUrl; // Full raw URL
|
||||
@property (nonatomic, copy, readonly) NSString *apiHost; // Parsed host
|
||||
@property (nonatomic, assign, readonly) NSInteger apiPort; // Parsed port
|
||||
@property (nonatomic, copy, readonly) NSString *signSecret;
|
||||
@property (nonatomic, copy, readonly) NSString *serviceDomain;
|
||||
|
||||
@property (nonatomic, assign, readonly) BOOL hasValidConfig;
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
//
|
||||
// DemoConfigLoader.m
|
||||
// NewHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
#import "DemoConfigLoader.h"
|
||||
|
||||
@implementation DemoConfigLoader {
|
||||
NSString *_appId;
|
||||
NSString *_primaryServiceHost;
|
||||
NSInteger _servicePort;
|
||||
NSString *_apiUrl; // Unified API URL
|
||||
NSString *_signSecret;
|
||||
NSString *_serviceDomain;
|
||||
BOOL _hasValidConfig;
|
||||
}
|
||||
|
||||
+ (instancetype)shared {
|
||||
@@ -28,23 +31,51 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
// 澶嶆潅閫昏緫锛氶厤缃姞杞介『搴忎负 Bundle > 鐜鍙橀噺锛涘苟瀵?accountID 杩涜鏈夋晥鎬ф牎楠?
|
||||
- (void)loadConfig {
|
||||
NSDictionary *dict = nil;
|
||||
_appId = @"";
|
||||
_apiUrl = @"";
|
||||
_signSecret = @"";
|
||||
_serviceDomain = @"";
|
||||
_hasValidConfig = NO;
|
||||
|
||||
NSDictionary *bundleDict = nil;
|
||||
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"DemoConfig" ofType:@"plist"];
|
||||
if (plistPath.length > 0) {
|
||||
dict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
|
||||
bundleDict = [NSDictionary dictionaryWithContentsOfFile:plistPath];
|
||||
}
|
||||
|
||||
_appId = dict[@"appId"] ?: @"";
|
||||
_primaryServiceHost = dict[@"primaryServiceHost"] ?: @"";
|
||||
_servicePort = [dict[@"servicePort"] integerValue] ?: 443;
|
||||
NSString *secret = dict[@"signSecret"] ?: @"";
|
||||
_signSecret = secret.length > 0 ? secret : nil;
|
||||
NSDictionary *env = [[NSProcessInfo processInfo] environment];
|
||||
|
||||
NSString *appId = bundleDict[@"appId"] ?: @"";
|
||||
NSString *apiUrl = bundleDict[@"apiUrl"] ?: @"";
|
||||
NSString *signSecret = bundleDict[@"signSecret"] ?: @"";
|
||||
NSString *serviceDomain = bundleDict[@"serviceDomain"] ?: @"";
|
||||
|
||||
NSString *envAppId = env[@"HTTPDNS_APP_ID"];
|
||||
NSString *envApiUrl = env[@"HTTPDNS_API_URL"];
|
||||
NSString *envSignSecret = env[@"HTTPDNS_SIGN_SECRET"];
|
||||
NSString *envServiceDomain = env[@"HTTPDNS_SERVICE_DOMAIN"];
|
||||
|
||||
if (envAppId.length > 0) appId = envAppId;
|
||||
if (envApiUrl.length > 0) apiUrl = envApiUrl;
|
||||
if (envSignSecret.length > 0) signSecret = envSignSecret;
|
||||
if (envServiceDomain.length > 0) serviceDomain = envServiceDomain;
|
||||
|
||||
if (appId.length > 0 && apiUrl.length > 0 && serviceDomain.length > 0) {
|
||||
_appId = appId;
|
||||
_apiUrl = apiUrl;
|
||||
_signSecret = signSecret;
|
||||
_serviceDomain = serviceDomain;
|
||||
_hasValidConfig = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)appId { return _appId; }
|
||||
- (NSString *)primaryServiceHost { return _primaryServiceHost; }
|
||||
- (NSInteger)servicePort { return _servicePort; }
|
||||
- (NSString *)apiUrl { return _apiUrl; }
|
||||
- (NSString *)signSecret { return _signSecret; }
|
||||
- (NSString *)serviceDomain { return _serviceDomain; }
|
||||
- (BOOL)hasValidConfig { return _hasValidConfig; }
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
//
|
||||
// DemoHttpdnsScenario.h
|
||||
// NewHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-23
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "DemoResolveModel.h"
|
||||
#import "HttpdnsEdgeService.h"
|
||||
#import "HttpdnsPublicConstant.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -13,7 +17,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@interface DemoHttpdnsScenarioConfig : NSObject <NSCopying>
|
||||
|
||||
@property (nonatomic, copy) NSString *host;
|
||||
@property (nonatomic, copy) NSString *queryType; // @"A", @"AAAA", @"both"
|
||||
@property (nonatomic, assign) HttpdnsQueryIPType ipType;
|
||||
|
||||
// HttpdnsEdgeService unsupported features removed:
|
||||
// @property (nonatomic, assign) BOOL httpsEnabled;
|
||||
// @property (nonatomic, assign) BOOL persistentCacheEnabled;
|
||||
// @property (nonatomic, assign) BOOL reuseExpiredIPEnabled;
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@@ -34,10 +43,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (instancetype)initWithDelegate:(id<DemoHttpdnsScenarioDelegate>)delegate;
|
||||
- (void)applyConfig:(DemoHttpdnsScenarioConfig *)config;
|
||||
- (void)resolve;
|
||||
- (void)resolveSyncNonBlocking;
|
||||
- (void)resolveSync;
|
||||
- (NSString *)logSnapshot;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
//
|
||||
//
|
||||
// DemoHttpdnsScenario.m
|
||||
// NewHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-23
|
||||
//
|
||||
|
||||
#import "DemoHttpdnsScenario.h"
|
||||
#import "DemoConfigLoader.h"
|
||||
#import <NewHTTPDNS/HttpdnsEdgeService.h>
|
||||
|
||||
@interface DemoHttpdnsScenarioConfig ()
|
||||
@end
|
||||
@@ -14,8 +15,12 @@
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_host = @"demo.cloudxdr.com";
|
||||
_queryType = @"A";
|
||||
NSString *serviceDomain = [DemoConfigLoader shared].serviceDomain;
|
||||
_host = serviceDomain.length > 0 ? serviceDomain : @"www.new.com";
|
||||
_ipType = HttpdnsQueryIPTypeBoth;
|
||||
// _httpsEnabled = YES;
|
||||
// _persistentCacheEnabled = YES;
|
||||
// _reuseExpiredIPEnabled = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -23,7 +28,10 @@
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
DemoHttpdnsScenarioConfig *cfg = [[[self class] allocWithZone:zone] init];
|
||||
cfg.host = self.host;
|
||||
cfg.queryType = self.queryType;
|
||||
cfg.ipType = self.ipType;
|
||||
// cfg.httpsEnabled = self.httpsEnabled;
|
||||
// cfg.persistentCacheEnabled = self.persistentCacheEnabled;
|
||||
// cfg.reuseExpiredIPEnabled = self.reuseExpiredIPEnabled;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@@ -48,76 +56,175 @@
|
||||
_logBuffer = [NSMutableString string];
|
||||
_logQueue = dispatch_queue_create("com.new.httpdns.demo.log", DISPATCH_QUEUE_SERIAL);
|
||||
[self buildService];
|
||||
[self applyConfig:_config];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)buildService {
|
||||
DemoConfigLoader *cfg = [DemoConfigLoader shared];
|
||||
self.service = [[HttpdnsEdgeService alloc]
|
||||
initWithAppId:cfg.appId
|
||||
primaryServiceHost:cfg.primaryServiceHost
|
||||
backupServiceHost:nil
|
||||
servicePort:cfg.servicePort
|
||||
signSecret:cfg.signSecret];
|
||||
[self appendLog:[NSString stringWithFormat:@"[init] appId=%@ host=%@:%ld", cfg.appId, cfg.primaryServiceHost, (long)cfg.servicePort]];
|
||||
if (cfg.hasValidConfig) {
|
||||
self.service = [[HttpdnsEdgeService alloc] initWithAppId:cfg.appId apiUrl:cfg.apiUrl signSecret:cfg.signSecret];
|
||||
[self log:[NSString stringWithFormat:@"Init HttpdnsEdgeService success!"]];
|
||||
[self log:[NSString stringWithFormat:@"== Config details =="]];
|
||||
[self log:[NSString stringWithFormat:@"appId: %@", cfg.appId]];
|
||||
[self log:[NSString stringWithFormat:@"apiUrl: %@", cfg.apiUrl]];
|
||||
[self log:[NSString stringWithFormat:@"serviceDomain: %@", cfg.serviceDomain]];
|
||||
[self log:[NSString stringWithFormat:@"signSecret length: %tu", cfg.signSecret.length]];
|
||||
} else {
|
||||
[self log:@"Init HttpdnsEdgeService failed! Missing required fields."];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applyConfig:(DemoHttpdnsScenarioConfig *)config {
|
||||
self.config = [config copy];
|
||||
self.model.host = self.config.host;
|
||||
self.model.ipType = self.config.ipType;
|
||||
[self log:[NSString stringWithFormat:@"Apply UI config: host=%@, ipType=%ld", self.config.host, (long)self.config.ipType]];
|
||||
}
|
||||
|
||||
- (void)resolve {
|
||||
[self resolveSyncNonBlocking];
|
||||
}
|
||||
|
||||
- (void)resolveSyncNonBlocking {
|
||||
NSString *host = self.config.host.length > 0 ? self.config.host : @"demo.cloudxdr.com";
|
||||
NSString *queryType = self.config.queryType.length > 0 ? self.config.queryType : @"A";
|
||||
NSString *queryHost = [self currentHost];
|
||||
HttpdnsQueryIPType ipType = self.config.ipType;
|
||||
NSTimeInterval startMs = [[NSDate date] timeIntervalSince1970] * 1000.0;
|
||||
|
||||
[self appendLog:[NSString stringWithFormat:@"[resolve] host=%@ type=%@", host, queryType]];
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.service resolveHost:host queryType:queryType completion:^(HttpdnsEdgeResolveResult *result, NSError *error) {
|
||||
[weakSelf handleResult:result host:host queryType:queryType start:startMs error:error];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)resolveSync {
|
||||
[self resolveSyncNonBlocking];
|
||||
}
|
||||
|
||||
- (void)handleResult:(HttpdnsEdgeResolveResult *)result
|
||||
host:(NSString *)host
|
||||
queryType:(NSString *)queryType
|
||||
start:(NSTimeInterval)startMs
|
||||
error:(NSError *)error {
|
||||
NSTimeInterval elapsedMs = [[NSDate date] timeIntervalSince1970] * 1000.0 - startMs;
|
||||
|
||||
if (error != nil) {
|
||||
[self appendLog:[NSString stringWithFormat:@"[error] %@", error.localizedDescription]];
|
||||
|
||||
[self log:[NSString stringWithFormat:@"--- Start Resolve Request ---"]];
|
||||
[self log:[NSString stringWithFormat:@"Target Host: %@", queryHost]];
|
||||
|
||||
if (ipType == HttpdnsQueryIPTypeBoth) {
|
||||
[self log:@"Query Type: BOTH (mapped to concurrent A & AAAA)"];
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
__block HttpdnsEdgeResolveResult *resA = nil;
|
||||
__block NSError *errA = nil;
|
||||
__block HttpdnsEdgeResolveResult *resAAAA = nil;
|
||||
__block NSError *errAAAA = nil;
|
||||
|
||||
dispatch_group_enter(group);
|
||||
[self.service resolveHost:queryHost queryType:@"A" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
resA = result;
|
||||
errA = error;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
dispatch_group_enter(group);
|
||||
[self.service resolveHost:queryHost queryType:@"AAAA" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
resAAAA = result;
|
||||
errAAAA = error;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
|
||||
[self log:@"--- Edge Service Callback Triggered (BOTH) ---"];
|
||||
HttpdnsEdgeResolveResult *merged = [[HttpdnsEdgeResolveResult alloc] init];
|
||||
merged.ipv4s = resA ? resA.ipv4s : @[];
|
||||
merged.ipv6s = resAAAA ? resAAAA.ipv6s : @[];
|
||||
merged.ttl = 0;
|
||||
if (resA && resAAAA) {
|
||||
merged.ttl = MIN(resA.ttl, resAAAA.ttl);
|
||||
} else if (resA) {
|
||||
merged.ttl = resA.ttl;
|
||||
} else if (resAAAA) {
|
||||
merged.ttl = resAAAA.ttl;
|
||||
}
|
||||
if (resA.requestId) merged.requestId = resA.requestId;
|
||||
else if (resAAAA.requestId) merged.requestId = resAAAA.requestId;
|
||||
|
||||
NSError *finalErr = nil;
|
||||
if (errA && errAAAA) {
|
||||
finalErr = errA;
|
||||
}
|
||||
|
||||
[self handleEdgeResult:merged error:finalErr host:queryHost ipType:ipType start:startMs];
|
||||
});
|
||||
|
||||
} else {
|
||||
[self appendLog:[NSString stringWithFormat:@"[result] requestId=%@ ipv4=%@ ipv6=%@ ttl=%ld elapsed=%.0fms",
|
||||
result.requestId, result.ipv4s, result.ipv6s, (long)result.ttl, elapsedMs]];
|
||||
NSString *qtype = (ipType == HttpdnsQueryIPTypeIpv6) ? @"AAAA" : @"A";
|
||||
[self log:[NSString stringWithFormat:@"Query Type: %@", qtype]];
|
||||
[self.service resolveHost:queryHost queryType:qtype completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
[self log:[NSString stringWithFormat:@"--- Edge Service Callback Triggered ---"]];
|
||||
[self handleEdgeResult:result error:error host:queryHost ipType:ipType start:startMs];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)logSnapshot {
|
||||
__block NSString *snapshot = @"";
|
||||
dispatch_sync(self.logQueue, ^{
|
||||
snapshot = [self.logBuffer copy];
|
||||
});
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
- (NSString *)currentHost {
|
||||
return self.config.host.length > 0 ? self.config.host : @"demo.cloudxdr.com";
|
||||
}
|
||||
|
||||
- (void)handleEdgeResult:(HttpdnsEdgeResolveResult *)result error:(NSError *)error host:(NSString *)host ipType:(HttpdnsQueryIPType)ipType start:(NSTimeInterval)startMs {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.model.host = host;
|
||||
if (result != nil) {
|
||||
self.model.ipv4s = result.ipv4s;
|
||||
self.model.ipv6s = result.ipv6s;
|
||||
self.model.ttlV4 = result.ipv4s.count > 0 ? result.ttl : 0;
|
||||
self.model.ttlV6 = result.ipv6s.count > 0 ? result.ttl : 0;
|
||||
self.model.error = nil;
|
||||
} else {
|
||||
self.model.ipType = ipType;
|
||||
|
||||
NSTimeInterval endMs = [[NSDate date] timeIntervalSince1970] * 1000.0;
|
||||
self.model.elapsedMs = endMs - startMs;
|
||||
|
||||
if (error) {
|
||||
self.model.ipv4s = @[];
|
||||
self.model.ipv6s = @[];
|
||||
self.model.error = error;
|
||||
self.model.ttlV4 = 0;
|
||||
self.model.ttlV6 = 0;
|
||||
[self log:[NSString stringWithFormat:@"Edge Resolve Failed!"]];
|
||||
[self log:[NSString stringWithFormat:@"Error domain: %@", error.domain]];
|
||||
[self log:[NSString stringWithFormat:@"Error code: %ld", (long)error.code]];
|
||||
[self log:[NSString stringWithFormat:@"Error description: %@", error.localizedDescription]];
|
||||
if (error.userInfo) {
|
||||
[self log:[NSString stringWithFormat:@"Error user info: %@", error.userInfo]];
|
||||
}
|
||||
} else {
|
||||
self.model.ipv4s = result.ipv4s ?: @[];
|
||||
self.model.ipv6s = result.ipv6s ?: @[];
|
||||
self.model.ttlV4 = result.ttl;
|
||||
self.model.ttlV6 = result.ttl;
|
||||
self.model.businessRequestResult = @"Waiting for request...";
|
||||
[self log:[NSString stringWithFormat:@"Edge Resolve Success!"]];
|
||||
[self log:[NSString stringWithFormat:@"Host: %@", host]];
|
||||
[self log:[NSString stringWithFormat:@"IPv4s: %@", result.ipv4s]];
|
||||
[self log:[NSString stringWithFormat:@"IPv6s: %@", result.ipv6s]];
|
||||
[self log:[NSString stringWithFormat:@"TTL: %ld", (long)result.ttl]];
|
||||
[self log:[NSString stringWithFormat:@"Request ID: %@", result.requestId]];
|
||||
|
||||
// Fire off a business request to demonstrate real usage if we have an IP
|
||||
if (result.ipv4s.count > 0 || result.ipv6s.count > 0) {
|
||||
[self log:@"\n--- Start Business Request Demo ---"];
|
||||
// Construct a URL for the business domain
|
||||
NSURL *businessURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@", host]];
|
||||
[self log:[NSString stringWithFormat:@"Requesting URL: %@", businessURL.absoluteString]];
|
||||
|
||||
[self.service requestURL:businessURL method:@"GET" headers:nil body:nil completion:^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable reqError) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (reqError) {
|
||||
NSString *err = [NSString stringWithFormat:@"Failed: %@", reqError.localizedDescription];
|
||||
[self log:[NSString stringWithFormat:@"Business Request %@", err]];
|
||||
self.model.businessRequestResult = err;
|
||||
} else {
|
||||
[self log:[NSString stringWithFormat:@"Business Request Success! Status Code: %ld", (long)response.statusCode]];
|
||||
NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
if (responseString.length > 200) {
|
||||
responseString = [[responseString substringToIndex:200] stringByAppendingString:@"... (truncated)"];
|
||||
}
|
||||
[self log:[NSString stringWithFormat:@"Response Body:\n%@", responseString]];
|
||||
self.model.businessRequestResult = [NSString stringWithFormat:@"HTTP %ld\n%@", (long)response.statusCode, responseString];
|
||||
}
|
||||
|
||||
id<DemoHttpdnsScenarioDelegate> bDelegate = self.delegate;
|
||||
if (bDelegate != nil) {
|
||||
[bDelegate scenario:self didUpdateModel:self.model];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
self.model.elapsedMs = elapsedMs;
|
||||
|
||||
|
||||
id<DemoHttpdnsScenarioDelegate> delegate = self.delegate;
|
||||
if (delegate != nil) {
|
||||
[delegate scenario:self didUpdateModel:self.model];
|
||||
@@ -125,9 +232,13 @@
|
||||
});
|
||||
}
|
||||
|
||||
- (void)appendLog:(NSString *)msg {
|
||||
if (msg.length == 0) return;
|
||||
NSString *line = [NSString stringWithFormat:@"%@ %@\n", [NSDate date], msg];
|
||||
// logger & ttl delegates removed since EdgeService lacks these protocol hooks in this SDK version
|
||||
|
||||
- (void)log:(NSString *)logStr {
|
||||
if (logStr.length == 0) {
|
||||
return;
|
||||
}
|
||||
NSString *line = [NSString stringWithFormat:@"%@ %@\n", [NSDate date], logStr];
|
||||
dispatch_async(self.logQueue, ^{
|
||||
[self.logBuffer appendString:line];
|
||||
id<DemoHttpdnsScenarioDelegate> delegate = self.delegate;
|
||||
@@ -139,12 +250,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
- (NSString *)logSnapshot {
|
||||
__block NSString *snapshot = @"";
|
||||
dispatch_sync(self.logQueue, ^{
|
||||
snapshot = [self.logBuffer copy];
|
||||
});
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
//
|
||||
// DemoResolveModel.h
|
||||
// NewHttpDNSTestDemo
|
||||
// TrustHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "HttpdnsRequest.h"
|
||||
#import "HttpdnsResult.h"
|
||||
|
||||
@interface DemoResolveModel : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSString *host;
|
||||
@property (nonatomic, assign) HttpdnsQueryIPType ipType;
|
||||
|
||||
@property (nonatomic, copy) NSArray<NSString *> *ipv4s;
|
||||
@property (nonatomic, copy) NSArray<NSString *> *ipv6s;
|
||||
|
||||
@property (nonatomic, assign) NSTimeInterval elapsedMs;
|
||||
@property (nonatomic, assign) NSTimeInterval ttlV4;
|
||||
@property (nonatomic, assign) NSTimeInterval ttlV6;
|
||||
@property (nonatomic, strong, nullable) NSError *error;
|
||||
|
||||
@property (nonatomic, copy) NSString *businessRequestResult;
|
||||
|
||||
- (void)updateWithResult:(HttpdnsResult *)result startTimeMs:(NSTimeInterval)startMs;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
//
|
||||
//
|
||||
// DemoResolveModel.m
|
||||
// NewHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
#import "DemoResolveModel.h"
|
||||
|
||||
@@ -9,15 +11,32 @@
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_host = @"demodemo.cloudxdr.com";
|
||||
_host = @"demo.cloudxdr.com";
|
||||
_ipType = HttpdnsQueryIPTypeBoth;
|
||||
_ipv4s = @[];
|
||||
_ipv6s = @[];
|
||||
_elapsedMs = 0;
|
||||
_ttlV4 = 0;
|
||||
_ttlV6 = 0;
|
||||
_error = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateWithResult:(HttpdnsResult *)result startTimeMs:(NSTimeInterval)startMs {
|
||||
NSTimeInterval now = [[NSDate date] timeIntervalSince1970] * 1000.0;
|
||||
_elapsedMs = MAX(0, now - startMs);
|
||||
if (result != nil) {
|
||||
_ipv4s = result.ips ?: @[];
|
||||
_ipv6s = result.ipv6s ?: @[];
|
||||
_ttlV4 = result.ttl;
|
||||
_ttlV6 = result.v6ttl;
|
||||
} else {
|
||||
_ipv4s = @[];
|
||||
_ipv6s = @[];
|
||||
_ttlV4 = 0;
|
||||
_ttlV6 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
//
|
||||
// DemoViewController.m
|
||||
//
|
||||
// DNSDemoViewController.m
|
||||
// NewHttpDNSTestDemo
|
||||
//
|
||||
// @author Created by Claude Code on 2025-10-05
|
||||
//
|
||||
|
||||
#import "DemoViewController.h"
|
||||
#import "DemoResolveModel.h"
|
||||
@@ -18,14 +20,20 @@
|
||||
@property (nonatomic, strong) UIStackView *stack;
|
||||
|
||||
@property (nonatomic, strong) UITextField *hostField;
|
||||
@property (nonatomic, strong) UISegmentedControl *queryTypeSeg;
|
||||
@property (nonatomic, strong) UISegmentedControl *ipTypeSeg;
|
||||
|
||||
// Removed toggles: HTTPS, Persist, Reuse
|
||||
|
||||
@property (nonatomic, strong) UILabel *elapsedLabel;
|
||||
@property (nonatomic, strong) UILabel *ttlLabel;
|
||||
|
||||
@property (nonatomic, strong) UITextView *resultTextView;
|
||||
|
||||
@property (nonatomic, weak) DemoLogViewController *presentedLogVC;
|
||||
|
||||
@property (nonatomic, strong) UIButton *btnResolve;
|
||||
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicator;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DemoViewController
|
||||
@@ -39,8 +47,7 @@
|
||||
self.scenarioConfig = [[DemoHttpdnsScenarioConfig alloc] init];
|
||||
[self buildUI];
|
||||
[self reloadUIFromModel:self.model];
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
|
||||
initWithTitle:@"日志" style:UIBarButtonItemStylePlain target:self action:@selector(onShowLog)];
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"日志" style:UIBarButtonItemStylePlain target:self action:@selector(onShowLog)];
|
||||
}
|
||||
|
||||
- (void)buildUI {
|
||||
@@ -67,36 +74,45 @@
|
||||
[self.stack.widthAnchor constraintEqualToAnchor:self.view.widthAnchor constant:-32]
|
||||
]];
|
||||
|
||||
// Host 输入
|
||||
UIStackView *row1 = [self labeledRow:@"Host"];
|
||||
self.hostField = [[UITextField alloc] init];
|
||||
self.hostField.placeholder = @"demodemo.cloudxdr.com";
|
||||
self.hostField.text = self.scenarioConfig.host;
|
||||
self.hostField.placeholder = @"YOUR_SERVICE_DOMAIN";
|
||||
self.hostField.text = self.scenarioConfig.host ?: @"";
|
||||
self.hostField.borderStyle = UITextBorderStyleRoundedRect;
|
||||
[row1 addArrangedSubview:self.hostField];
|
||||
[self.stack addArrangedSubview:row1];
|
||||
|
||||
// 查询类型
|
||||
UIStackView *row2 = [self labeledRow:@"类型"];
|
||||
self.queryTypeSeg = [[UISegmentedControl alloc] initWithItems:@[@"A (IPv4)", @"AAAA (IPv6)", @"both"]];
|
||||
self.queryTypeSeg.selectedSegmentIndex = 0;
|
||||
[self.queryTypeSeg addTarget:self action:@selector(onQueryTypeChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
[row2 addArrangedSubview:self.queryTypeSeg];
|
||||
UIStackView *row2 = [self labeledRow:@"IP Type"];
|
||||
self.ipTypeSeg = [[UISegmentedControl alloc] initWithItems:@[@"IPv4", @"IPv6", @"Both"]];
|
||||
self.ipTypeSeg.selectedSegmentIndex = [self segmentIndexForIpType:self.scenarioConfig.ipType];
|
||||
[self.ipTypeSeg addTarget:self action:@selector(onIPTypeChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
[row2 addArrangedSubview:self.ipTypeSeg];
|
||||
[self.stack addArrangedSubview:row2];
|
||||
|
||||
// 解析按钮
|
||||
UIButton *btnResolve = [self filledButton:@"Resolve" action:@selector(onResolve)];
|
||||
[self.stack addArrangedSubview:btnResolve];
|
||||
// Removed options stack from UI
|
||||
|
||||
UIStackView *actions = [[UIStackView alloc] init];
|
||||
actions.axis = UILayoutConstraintAxisHorizontal;
|
||||
actions.spacing = 12;
|
||||
actions.distribution = UIStackViewDistributionFillEqually;
|
||||
[self.stack addArrangedSubview:actions];
|
||||
|
||||
self.btnResolve = [self filledButton:@"Resolve IP & Test Request" action:@selector(onResolve)];
|
||||
[actions addArrangedSubview:self.btnResolve];
|
||||
|
||||
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
|
||||
self.activityIndicator.hidesWhenStopped = YES;
|
||||
[actions addArrangedSubview:self.activityIndicator];
|
||||
|
||||
// 耗时 / TTL
|
||||
UIStackView *info = [self labeledRow:@"Info"];
|
||||
self.elapsedLabel = [self monoLabel:@"elapsed: - ms"];
|
||||
self.ttlLabel = [self monoLabel:@"ttl: -"];
|
||||
self.ttlLabel = [self monoLabel:@"ttl v4/v6: -/- s"];
|
||||
[info addArrangedSubview:self.elapsedLabel];
|
||||
[info addArrangedSubview:self.ttlLabel];
|
||||
[self.stack addArrangedSubview:info];
|
||||
if (@available(iOS 11.0, *)) { [self.stack setCustomSpacing:24 afterView:info]; }
|
||||
|
||||
|
||||
// 结果
|
||||
UILabel *resultTitle = [[UILabel alloc] init];
|
||||
resultTitle.text = @"结果";
|
||||
resultTitle.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
|
||||
@@ -108,66 +124,18 @@
|
||||
if (@available(iOS 13.0, *)) {
|
||||
self.resultTextView.font = [UIFont monospacedSystemFontOfSize:12 weight:UIFontWeightRegular];
|
||||
self.resultTextView.textColor = [UIColor labelColor];
|
||||
} else {
|
||||
self.resultTextView.font = [UIFont systemFontOfSize:12];
|
||||
self.resultTextView.textColor = [UIColor blackColor];
|
||||
}
|
||||
self.resultTextView.backgroundColor = [UIColor clearColor];
|
||||
self.resultTextView.textContainerInset = UIEdgeInsetsMake(8, 12, 8, 12);
|
||||
[self.stack addArrangedSubview:self.resultTextView];
|
||||
[self.resultTextView.heightAnchor constraintEqualToConstant:320].active = YES;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)onQueryTypeChanged:(UISegmentedControl *)seg {
|
||||
NSArray *types = @[@"A", @"AAAA", @"both"];
|
||||
self.scenarioConfig.queryType = types[seg.selectedSegmentIndex];
|
||||
[self.scenario applyConfig:self.scenarioConfig];
|
||||
}
|
||||
|
||||
- (void)onResolve {
|
||||
[self.view endEditing:YES];
|
||||
NSString *host = self.hostField.text.length > 0 ? self.hostField.text : @"demodemo.cloudxdr.com";
|
||||
self.scenarioConfig.host = host;
|
||||
[self.scenario applyConfig:self.scenarioConfig];
|
||||
[self.scenario resolve];
|
||||
}
|
||||
|
||||
- (void)onShowLog {
|
||||
DemoLogViewController *logVC = [DemoLogViewController new];
|
||||
[logVC setInitialText:[self.scenario logSnapshot]];
|
||||
self.presentedLogVC = logVC;
|
||||
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:logVC];
|
||||
nav.modalPresentationStyle = UIModalPresentationAutomatic;
|
||||
[self presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)reloadUIFromModel:(DemoResolveModel *)model {
|
||||
self.model = model;
|
||||
if (![self.hostField isFirstResponder]) {
|
||||
self.hostField.text = model.host;
|
||||
}
|
||||
self.elapsedLabel.text = [NSString stringWithFormat:@"elapsed: %.0f ms", model.elapsedMs];
|
||||
self.ttlLabel.text = [NSString stringWithFormat:@"ttl v4/v6: %.0f/%.0f s", model.ttlV4, model.ttlV6];
|
||||
self.resultTextView.text = [self buildResultText:model];
|
||||
}
|
||||
|
||||
- (NSString *)buildResultText:(DemoResolveModel *)model {
|
||||
if (model.error != nil) {
|
||||
return [NSString stringWithFormat:@"Error:\n%@", model.error.localizedDescription];
|
||||
}
|
||||
NSDictionary *dict = @{
|
||||
@"host": model.host ?: @"",
|
||||
@"elapsed": [NSString stringWithFormat:@"%.0f ms", model.elapsedMs],
|
||||
@"ttl": @{ @"v4": @(model.ttlV4), @"v6": @(model.ttlV6) },
|
||||
@"ipv4": model.ipv4s ?: @[],
|
||||
@"ipv6": model.ipv6s ?: @[]
|
||||
};
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
|
||||
if (data == nil) return @"{}";
|
||||
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
- (UIStackView *)labeledRow:(NSString *)title {
|
||||
UIStackView *row = [[UIStackView alloc] init];
|
||||
row.axis = UILayoutConstraintAxisHorizontal;
|
||||
@@ -185,6 +153,8 @@
|
||||
l.text = text;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
l.font = [UIFont monospacedSystemFontOfSize:12 weight:UIFontWeightRegular];
|
||||
} else {
|
||||
l.font = [UIFont systemFontOfSize:12];
|
||||
}
|
||||
return l;
|
||||
}
|
||||
@@ -200,6 +170,128 @@
|
||||
return b;
|
||||
}
|
||||
|
||||
- (UIButton *)borderButton:(NSString *)title action:(SEL)sel {
|
||||
UIButton *b = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[b setTitle:title forState:UIControlStateNormal];
|
||||
b.layer.borderWidth = 1;
|
||||
b.layer.borderColor = [UIColor systemBlueColor].CGColor;
|
||||
b.layer.cornerRadius = 8;
|
||||
[b.heightAnchor constraintEqualToConstant:44].active = YES;
|
||||
[b addTarget:self action:sel forControlEvents:UIControlEventTouchUpInside];
|
||||
return b;
|
||||
}
|
||||
|
||||
- (UIView *)switchItem:(NSString *)title action:(SEL)sel out:(UISwitch * __strong *)outSwitch {
|
||||
UIStackView *box = [[UIStackView alloc] init];
|
||||
box.axis = UILayoutConstraintAxisVertical;
|
||||
box.alignment = UIStackViewAlignmentCenter;
|
||||
UILabel *l = [[UILabel alloc] init];
|
||||
l.text = title;
|
||||
UISwitch *s = [[UISwitch alloc] init];
|
||||
[s addTarget:self action:sel forControlEvents:UIControlEventValueChanged];
|
||||
[box addArrangedSubview:s];
|
||||
[box addArrangedSubview:l];
|
||||
if (outSwitch != NULL) {
|
||||
*outSwitch = s;
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
- (NSInteger)segmentIndexForIpType:(HttpdnsQueryIPType)ipType {
|
||||
switch (ipType) {
|
||||
case HttpdnsQueryIPTypeIpv4: { return 0; }
|
||||
case HttpdnsQueryIPTypeIpv6: { return 1; }
|
||||
default: { return 2; }
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)onIPTypeChanged:(UISegmentedControl *)seg {
|
||||
HttpdnsQueryIPType type = HttpdnsQueryIPTypeBoth;
|
||||
switch (seg.selectedSegmentIndex) {
|
||||
case 0: type = HttpdnsQueryIPTypeIpv4; break;
|
||||
case 1: type = HttpdnsQueryIPTypeIpv6; break;
|
||||
default: type = HttpdnsQueryIPTypeBoth; break;
|
||||
}
|
||||
self.model.ipType = type;
|
||||
self.scenarioConfig.ipType = type;
|
||||
[self.scenario applyConfig:self.scenarioConfig];
|
||||
}
|
||||
|
||||
// Toggles delegates removed
|
||||
|
||||
- (void)onResolve {
|
||||
[self.view endEditing:YES];
|
||||
|
||||
// Show loading state
|
||||
self.btnResolve.enabled = NO;
|
||||
[self.btnResolve setTitle:@"Resolving..." forState:UIControlStateNormal];
|
||||
[self.activityIndicator startAnimating];
|
||||
|
||||
NSString *host = self.hostField.text.length > 0 ? self.hostField.text : @"";
|
||||
self.model.host = host;
|
||||
self.scenarioConfig.host = host;
|
||||
[self.scenario applyConfig:self.scenarioConfig];
|
||||
[self.scenario resolve];
|
||||
}
|
||||
|
||||
- (void)onShowLog {
|
||||
DemoLogViewController *logVC = [DemoLogViewController new];
|
||||
[logVC setInitialText:[self.scenario logSnapshot]];
|
||||
self.presentedLogVC = logVC;
|
||||
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:logVC];
|
||||
nav.modalPresentationStyle = UIModalPresentationAutomatic;
|
||||
[self presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)reloadUIFromModel:(DemoResolveModel *)model {
|
||||
self.model = model;
|
||||
|
||||
// Hide loading state
|
||||
self.btnResolve.enabled = YES;
|
||||
[self.btnResolve setTitle:@"Resolve IP & Test Request" forState:UIControlStateNormal];
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
if (![self.hostField isFirstResponder]) {
|
||||
self.hostField.text = model.host;
|
||||
}
|
||||
NSInteger segIndex = [self segmentIndexForIpType:model.ipType];
|
||||
if (self.ipTypeSeg.selectedSegmentIndex != segIndex) {
|
||||
self.ipTypeSeg.selectedSegmentIndex = segIndex;
|
||||
}
|
||||
self.elapsedLabel.text = [NSString stringWithFormat:@"elapsed: %.0f ms", model.elapsedMs];
|
||||
self.ttlLabel.text = [NSString stringWithFormat:@"ttl v4/v6: %.0f/%.0f s", model.ttlV4, model.ttlV6];
|
||||
self.resultTextView.text = [self buildJSONText:model];
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)buildJSONText:(DemoResolveModel *)model {
|
||||
NSString *ipTypeStr = @"both";
|
||||
switch (model.ipType) {
|
||||
case HttpdnsQueryIPTypeIpv4: { ipTypeStr = @"ipv4"; break; }
|
||||
case HttpdnsQueryIPTypeIpv6: { ipTypeStr = @"ipv6"; break; }
|
||||
default: { ipTypeStr = @"both"; break; }
|
||||
}
|
||||
NSMutableDictionary *dict = [@{
|
||||
@"host": model.host ?: @"",
|
||||
@"ipType": ipTypeStr,
|
||||
@"elapsedMs": @(model.elapsedMs),
|
||||
@"ttl": @{ @"v4": @(model.ttlV4), @"v6": @(model.ttlV6) },
|
||||
@"ipv4": model.ipv4s ?: @[],
|
||||
@"ipv6": model.ipv6s ?: @[]
|
||||
} mutableCopy];
|
||||
if (model.businessRequestResult.length > 0) {
|
||||
dict[@"businessRequestResult"] = model.businessRequestResult;
|
||||
}
|
||||
NSError *err = nil;
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&err];
|
||||
if (data == nil || err != nil) {
|
||||
return [NSString stringWithFormat:@"{\n \"error\": \"%@\"\n}", err.localizedDescription ?: @"json serialize failed"];
|
||||
}
|
||||
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
#pragma mark - DemoHttpdnsScenarioDelegate
|
||||
|
||||
- (void)scenario:(DemoHttpdnsScenario *)scenario didUpdateModel:(DemoResolveModel *)model {
|
||||
|
||||
@@ -1,33 +1,41 @@
|
||||
# HTTPDNS iOS SDK (SNI Hidden v1.0.0)
|
||||
# HTTPDNS iOS SDK (Edge Service v1.0.0)
|
||||
|
||||
## 1. Init
|
||||
|
||||
Import the umbrella header and initialize the `HttpdnsEdgeService`.
|
||||
|
||||
```objc
|
||||
#import <TrustHTTPDNS/TrustHTTPDNS.h>
|
||||
#import <NewHttpDNS/NewHttpDNS.h>
|
||||
|
||||
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
|
||||
initWithAppId:@"app1flndpo9"
|
||||
apiUrl:@"https://httpdns.deepwaf.xyz:8445"
|
||||
signSecret:@"your-sign-secret"];
|
||||
```
|
||||
|
||||
## 2. Resolve
|
||||
|
||||
Support for `A` and `AAAA` query types.
|
||||
|
||||
```objc
|
||||
[service resolveHost:@"api.business.com" queryType:@"A" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
[service resolveHost:@"demo.cloudxdr.com" queryType:@"A" completion:^(HttpdnsEdgeResolveResult * _Nullable result, NSError * _Nullable error) {
|
||||
if (error != nil) {
|
||||
NSLog(@"Resolve error: %@", error.localizedDescription);
|
||||
return;
|
||||
}
|
||||
NSLog(@"requestId=%@ ipv4=%@ ipv6=%@ ttl=%ld", result.requestId, result.ipv4s, result.ipv6s, (long)result.ttl);
|
||||
}];
|
||||
```
|
||||
|
||||
## 3. Official Request Adapter (IP + Host)
|
||||
> [!NOTE]
|
||||
> If you need to resolve both IPv4 and IPv6 simultaneously (Dual Stack), please perform two separate requests or implement concurrent logic, as the Edge backend strictly validates `qtype` as either `A` or `AAAA`.
|
||||
|
||||
## 3. Business Request (SNI Support)
|
||||
|
||||
Use the built-in request adapter to connect to resolved IPs via HTTPS while preserving the `Host` header for SNI validation.
|
||||
|
||||
```objc
|
||||
NSURL *url = [NSURL URLWithString:@"https://api.business.com/v1/ping"];
|
||||
NSURL *url = [NSURL URLWithString:@"https://demo.cloudxdr.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;
|
||||
@@ -36,23 +44,15 @@ NSURL *url = [NSURL URLWithString:@"https://api.business.com/v1/ping"];
|
||||
}];
|
||||
```
|
||||
|
||||
Behavior is fixed:
|
||||
- Resolve by `/resolve`.
|
||||
- Connect to resolved IP over HTTPS.
|
||||
- Keep `Host` header as business domain.
|
||||
- No fallback to domain direct request.
|
||||
## 4. Key Improvements
|
||||
- **Security Policy**: Automatically handles certificate validation for IP-based HTTPS requests (no SNI mismatch).
|
||||
- **Modern API**: Asynchronous block-based API for efficient network operations.
|
||||
- **Clean Configuration**: Uses HMAC-SHA256 signing for secure resolution requests.
|
||||
|
||||
## 4. Public Errors
|
||||
## 5. Deprecated Parameters
|
||||
|
||||
- `NO_IP_AVAILABLE`
|
||||
- `TLS_EMPTY_SNI_FAILED`
|
||||
- `HOST_ROUTE_REJECTED`
|
||||
- `RESOLVE_SIGN_INVALID`
|
||||
|
||||
## 5. Removed Public Params
|
||||
|
||||
Do not expose legacy public parameters:
|
||||
Do NOT use legacy parameters from `HttpDnsService`:
|
||||
- `accountId`
|
||||
- `serviceDomain`
|
||||
- `endpoint`
|
||||
- `secretKey`
|
||||
- `aesSecretKey`
|
||||
- `serviceDomain` (use `dn` parameter or `host` in API)
|
||||
|
||||
187
HttpDNSSDK/sdk/ios/build.log
Normal file
187
HttpDNSSDK/sdk/ios/build.log
Normal file
File diff suppressed because one or more lines are too long
41
HttpDNSSDK/sdk/ios/test_qtype.py
Normal file
41
HttpDNSSDK/sdk/ios/test_qtype.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import time
|
||||
import hmac
|
||||
import hashlib
|
||||
import uuid
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import ssl
|
||||
|
||||
def test_qtype(qtype):
|
||||
appId = 'app1flndpo9'
|
||||
host = 'demo.cloudxdr.com'
|
||||
signSecret = 'ss_67fb8471a45b'
|
||||
|
||||
exp = str(int(time.time()) + 600)
|
||||
nonce = uuid.uuid4().hex.replace('-', '')
|
||||
|
||||
raw = f"{appId}|{host.lower()}|{qtype.upper()}|{exp}|{nonce}"
|
||||
sign = hmac.new(signSecret.encode(), raw.encode(), hashlib.sha256).hexdigest()
|
||||
|
||||
q_enc = urllib.parse.quote_plus(qtype)
|
||||
url = f"https://httpdns.deepwaf.xyz:8445/resolve?appId={appId}&dn={host}&qtype={q_enc}&sid=test&sdk_version=ios&os=ios&exp={exp}&nonce={nonce}&sign={sign}"
|
||||
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
try:
|
||||
req = urllib.request.Request(url)
|
||||
with urllib.request.urlopen(req, context=ctx, timeout=5) as response:
|
||||
print(f"Testing qtype '{qtype}': HTTP {response.status} - {response.read().decode('utf-8')}")
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f"Testing qtype '{qtype}': HTTP {e.code} - {e.read().decode('utf-8')}")
|
||||
except Exception as e:
|
||||
print(f"Testing qtype '{qtype}': Error {e}")
|
||||
|
||||
test_qtype("A")
|
||||
test_qtype("ADDNS")
|
||||
test_qtype("BOTH")
|
||||
test_qtype("A,AAAA")
|
||||
test_qtype("4,6")
|
||||
test_qtype("AAAA")
|
||||
Reference in New Issue
Block a user