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` 字段匹配。
|
||||
Reference in New Issue
Block a user