本文档介绍如何在 HarmonyOS 原生网络库、RCP 和 webiew 中通过DoH,接入HTTPDNS。
1. 前言
在 HarmonyOS 平台,推荐您优先通过HarmonyOS SDK接入。若您由于某些原因无法引入 SDK,您可以通过 DoH的接入,本文聚焦 HarmonyOS 下“网络库或组件”的 DoH接入方式,涵盖:
鸿蒙的网络组件只支持配置一个 DoH 链接,为提高业务稳定性,建议做好Local DNS降级
Network Kit 和 RCP 库中,不支持配置自动降级到 Local DNS 的模式,请做好应用侧降级,参考接入方案的示例代码。
ArkWeb 可以通过设置
SecureDnsMode
为AUTO
开启允许自动降级到 Local DNS。
Network Kit 和 RCP 的 DoH 配置会受
addCustomDnsRule
自定义解析规则的影响,如果您通过addCustomDnsRule
配置了某些域名的解析,那么该域名将不会再通过DoH 解析。
2. 前提条件
通过 DoH 接入 HarmonyOS 下“网络库或组件”前,请您确保已经完成配置DoH服务。
3. Network Kit(httpRequest)接入 DoH
通过 Network Kit 在单次请求上指定http.request(options: http.RequestOptions)
中的options.dnsOverHttps: string
为您的 DoH 接入点,即可开启 DoH,无需改造全局会话。示例代码如下:
import http from '@ohos.net.http';
const httpRequest: http.HttpRequest = http.createHttp();
// 这里请替换为您自己的 DoH URL
const DOH_ENDPOINT = 'https://xxxxx.aliyunhttpdns.com/dns-query';
const isDoHFailure = (err: ErrorDetails): boolean => {
const code = String(err.code ?? '');
const msg = String(err.message ?? '');
return /(couldn'?t\s+resolve\s+host\s+name|resolve\s+host\s+name|dns|resolve|name\s*not\s*resolved|EAI_AGAIN)/i.test(msg) || /DNS/i.test(code);
};
httpRequest.request(this.urlInput, {
method: http.RequestMethod.GET,
connectTimeout: 3000,
readTimeout: 3000,
dnsOverHttps: DOH_ENDPOINT,
}).then((res: http.HttpResponse) => {
console.log('DoH request success:', res);
}).catch((err: ErrorDetails) => {
if (isDoHFailure(err)) {
console.error('DoH request error, falling back to local DNS:', err);
httpRequest.request(this.urlInput, {
method: http.RequestMethod.GET,
connectTimeout: 3000,
readTimeout: 3000,
}).then((fallbackRes: http.HttpResponse) => {
console.log('Fallback request success:', fallbackRes);
}).catch((fallbackErr: ErrorDetails) => {
console.error('Fallback request error:', fallbackErr);
});
} else {
console.error('Request error:', err);
}
});
4. Remote Communication Kit(RCP)接入 DoH
RCP 网络库支持在全局 Session
和 单个 Request
两个不同粒度下接入 DoH, 下面介绍对应的接入方式。
4.1 Session 接入 DoH
通过 RCP 在会话层面上指定SessionConfiguration.requestConfiguration.dns.dnsOverHttps
为您的 DoH 接入点,即可开启 DoH,无需改造全局会话。示例代码如下:
import { rcp } from '@kit.RemoteCommunicationKit';
import type { BusinessError } from '@ohos.base';
private isDoHFailure(err: BusinessError): boolean {
const code: string = err.code ? String(err.code) : '';
const msg: string = String(err.data);
return /(couldn'?t\s+resolve\s+host\s+name|resolve\s+host\s+name|dns|resolve|name\s*not\s*resolved|EAI_AGAIN)/i.test(msg) || /DNS/i.test(code);
}
async sendRequest() {
try {
const dohConfig: rcp.DnsOverHttpsConfiguration = {
url: 'https://xxxxx.aliyunhttpdns.com/dns-query',
skipCertificatesValidation: false,
};
const dohSession = rcp.createSession({
requestConfiguration: {
dns: { dnsOverHttps: dohConfig },
transfer: { timeout: { connectMs: 3000, transferMs: 8000 } },
},
});
const resp = await dohSession.get(this.urlInput);
console.info('DoH request success, status=', resp.statusCode);
console.info('Response:', JSON.stringify(resp));
} catch (err) {
if (this.isDoHFailure(err)) {
console.error('DoH request error, falling back to local DNS:', err);
try {
const localSession = rcp.createSession({
requestConfiguration: { transfer: { timeout: { connectMs: 3000, transferMs: 8000 } } },
});
const fb = await localSession.get(this.urlInput);
console.info('Fallback (local DNS) success, status=', fb.statusCode);
console.info('Fallback response:', JSON.stringify(fb));
} catch (fallbackErr) {
console.error('Fallback (local DNS) request error:', fallbackErr);
}
} else {
console.error('Request error (non-DoH):', err);
}
}
}
4.2 Request 接入 DoH
通过 RCP 在单次请求上指定 request.configuration.dns.dnsOverHttps
为您的 DoH 接入点,即可开启 DoH,无需改造全局会话。示例代码如下:
import { rcp } from '@kit.RemoteCommunicationKit';
import type { BusinessError } from '@ohos.base';
async sendRequest() {
try {
const dohConfig: rcp.DnsOverHttpsConfiguration = {
url: 'https://xxxxx.aliyunhttpdns.com/dns-query',
skipCertificatesValidation: false,
};
const session = rcp.createSession({
requestConfiguration: {
transfer: { timeout: { connectMs: 3000, transferMs: 8000 } },
},
});
const perReq = new rcp.Request(this.urlInput, 'GET', undefined, undefined, undefined, undefined, {
dns: { dnsOverHttps: dohConfig },
});
const resp = await session.fetch(perReq);
console.info('DoH request success, status=', resp.statusCode);
console.info('Response:', JSON.stringify(resp));
} catch (err) {
if (this.isDoHFailure(err)) {
console.error('DoH request error, falling back to local DNS:', err);
try {
const localSession = rcp.createSession({
requestConfiguration: { transfer: { timeout: { connectMs: 3000, transferMs: 8000 } } },
});
const fb = await localSession.get(this.urlInput);
console.info('Fallback (local DNS) success, status=', fb.statusCode);
console.info('Fallback response:', JSON.stringify(fb));
} catch (fallbackErr) {
console.error('Fallback (local DNS) request error:', fallbackErr);
}
} else {
console.error('Request error (non-DoH):', err);
}
}
}
5. WebView 的 DoH 策略
通过 webview.WebviewController.setHttpDns
指定您的 DoH URL,示例代码如下:
import { webview } from '@kit.ArkWeb';
webview.WebviewController.setHttpDns(webview.SecureDnsMode.AUTO, 'https://xxxxx.aliyunhttpdns.com/dns-query');
鸿蒙的网络组件只支持配置一个 DoH 链接,为提高业务稳定性,建议做好Local DNS降级,ArkWeb 可以通过设置SecureDnsMode
为AUTO
开启允许自动降级到 Local DNS。
6. 总结
通过本文档介绍的步骤,您可以成功在 鸿蒙网络库中集成DNS over HTTPS (DoH)功能,从而显著提升应用程序的安全性和用户隐私保护能力。配置后可通过将手机 WIFI 网络的 DNS 服务器设置为一个无效地址,观察业务请求是否依然能够正常发起,以此判断 DoH 是否接入成功。