当前最佳实践文档只针对结合使用时,如何使用HTTPDNS解析出的IP,关于HTTPDNS本身的解析服务,请先查看iOS SDK 开发手册。
背景信息
由于WebView
并未暴露处设置 DNS 的接口,因而在WebView
场景下使用HTTPDNS
存在很多限制,但如果接入WEEX
,则可以较好地植入HTTPDNS
,本文主要介绍在WEEX
场景下接入HTTPDNS
的方案细节。
在WEEX
运行时环境下,所有的逻辑最终都会转换到Native Runtime
中执行,网络请求也不例外。同时WEEX
也提供了自定义相应实现的接口,通过重写网络请求适配器,我们可以较为简单地接入 HTTPDNS 。在 WEEX 运行环境中,主要有两种网络请求:
Stream网络请求+HTTPDNS
新版本Weex SDK实现
下面以 weex iOS 0.17.0
版本为例:
Stream
网络请求在 iOS 端最终会通过 WXResourceRequestHandlerDefaultImpl
完成,同时 WEEX
也提供了相应的接口自定义网络请求适配器。具体的逻辑如下:
创建自定义网络请求适配器,实现
WXResourceRequestHandlerHttpDnsImpl
接口#import <WeexSDK/WeexSDK.h> #import "WXResourceRequestHandlerDefaultImpl.h" @interface WXResourceRequestHandlerHttpDnsImpl : WXResourceRequestHandlerDefaultImpl<WXResourceRequestHandler,NSURLSessionDelegate> @end
在
WEEX
初始化时注册自定义网络适配器,替换默认适配器:[WXSDKEngine registerHandler:[WXResourceRequestHandlerHttpDnsImpl new] withProtocol:@protocol(WXResourceRequestHandler)];
下面以 weex iOS 0.7.0
版本为例:
Stream
网络请求在 iOS 端最终会通过WXNetworkDefaultImpl完成,同时WEEX
也提供了相应的接口自定义网络请求适配器。具体的逻辑如下:
创建自定义网络请求适配器,实现
WXNetworkHttpDnsImpl
接口#import <WeexSDK/WeexSDK.h> #import "WXNetworkDefaultImpl.h" @interface WXNetworkHttpDnsImpl : NSObject<WXNetworkProtocol, WXModuleProtocol , NSURLSessionDelegate> @end
在
WEEX
初始化时注册自定义网络适配器,替换默认适配器:[WXSDKEngine registerHandler:[WXNetworkHttpDnsImpl new] withProtocol:@protocol(WXNetworkProtocol)];
之后的网络请求都会通过
WXNetworkHttpDnsImpl
实现,所以只需要在WXNetworkHttpDnsImpl
中植入 HTTPDNS 逻辑即可,具体逻辑可以参考如下代码:#import "WXResourceRequestHandlerHttpDnsImpl.h" #import "WXThreadSafeMutableDictionary.h" #import "WXAppConfiguration.h" #import <AlicloudHttpDNS/AlicloudHttpDNS.h> @interface WXResourceRequestHandlerHttpDnsImpl () <NSURLSessionDataDelegate> @property (nonatomic, strong) NSMutableURLRequest *request; @end @implementation WXResourceRequestHandlerHttpDnsImpl { NSURLSession *_session; WXThreadSafeMutableDictionary<NSURLSessionDataTask *, id<WXResourceRequestDelegate>> *_delegates; } #pragma mark - WXResourceRequestHandler - (void)sendRequest:(WXResourceRequest *)theRequest withDelegate:(id<WXResourceRequestDelegate>)delegate { self.request = [theRequest mutableCopy]; NSString *originalUrl = [theRequest.URL absoluteString]; NSString *originalHost = theRequest.URL.host; // 初始化httpdns实例 HttpDnsService *httpdns = [HttpDnsService sharedInstance]; NSString *ip = [httpdns getIpByHostAsync:theRequest.URL.host]; if (ip) { // 通过HTTPDNS获取IP成功,进行URL替换和HOST头设置 NSLog(@"Get IP(%@) for host(%@) from HTTPDNS Successfully!", ip, originalHost); NSRange hostFirstRange = [originalUrl rangeOfString:originalHost]; if (NSNotFound != hostFirstRange.location) { NSString *newUrl = [originalUrl stringByReplacingCharactersInRange:hostFirstRange withString:ip]; NSLog(@"New URL: %@", newUrl); self.request.URL = [NSURL URLWithString:newUrl]; [self.request setValue:originalHost forHTTPHeaderField:@"host"]; } } if (!_session) { NSURLSessionConfiguration *urlSessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; if ([WXAppConfiguration customizeProtocolClasses].count > 0) { NSArray *defaultProtocols = urlSessionConfig.protocolClasses; urlSessionConfig.protocolClasses = [[WXAppConfiguration customizeProtocolClasses] arrayByAddingObjectsFromArray:defaultProtocols]; } _session = [NSURLSession sessionWithConfiguration:urlSessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; _delegates = [WXThreadSafeMutableDictionary new]; } NSURLSessionDataTask *task = [_session dataTaskWithRequest:theRequest]; theRequest.taskIdentifier = task; [_delegates setObject:delegate forKey:task]; [task resume]; } - (void)cancelRequest:(WXResourceRequest *)request { if ([request.taskIdentifier isKindOfClass:[NSURLSessionTask class]]) { NSURLSessionTask *task = (NSURLSessionTask *)request.taskIdentifier; [task cancel]; [_delegates removeObjectForKey:task]; } } #pragma mark - NSURLSessionTaskDelegate & NSURLSessionDataDelegate - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { id<WXResourceRequestDelegate> delegate = [_delegates objectForKey:task]; [delegate request:(WXResourceRequest *)task.originalRequest didSendData:bytesSent totalBytesToBeSent:totalBytesExpectedToSend]; } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { id<WXResourceRequestDelegate> delegate = [_delegates objectForKey:task]; [delegate request:(WXResourceRequest *)task.originalRequest didReceiveResponse:(WXResourceResponse *)response]; completionHandler(NSURLSessionResponseAllow); } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveData:(NSData *)data { id<WXResourceRequestDelegate> delegate = [_delegates objectForKey:task]; [delegate request:(WXResourceRequest *)task.originalRequest didReceiveData:data]; } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { id<WXResourceRequestDelegate> delegate = [_delegates objectForKey:task]; if (error) { [delegate request:(WXResourceRequest *)task.originalRequest didFailWithError:error]; }else { [delegate requestDidFinishLoading:(WXResourceRequest *)task.originalRequest]; } [_delegates removeObjectForKey:task]; } #ifdef __IPHONE_10_0 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics { id<WXResourceRequestDelegate> delegate = [_delegates objectForKey:task]; [delegate request:(WXResourceRequest *)task.originalRequest didFinishCollectingMetrics:metrics]; } #endif - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { /* * 创建证书校验策略 */ NSMutableArray *policies = [NSMutableArray array]; if (domain) { [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)]; } else { [policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()]; } /* * 绑定校验策略到服务端的证书上 */ SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef) policies); /* * 评估当前serverTrust是否可信任, * 官方建议在result = kSecTrustResultUnspecified 或 kSecTrustResultProceed * 的情况下serverTrust可以被验证通过,https://developer.apple.com/library/ios/technotes/tn2232/_index.html * 关于SecTrustResultType的详细信息请参考SecTrust.h */ SecTrustResultType result; SecTrustEvaluate(serverTrust, &result); return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); } #pragma mark - NSURLSessionTaskDelegate - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *_Nullable))completionHandler { if (!challenge) { return; } NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; NSURLCredential *credential = nil; /* * 获取原始域名信息。 */ NSString *host = [[self.request allHTTPHeaderFields] objectForKey:@"host"]; if (!host) { host = self.request.URL.host; } if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) { disposition = NSURLSessionAuthChallengeUseCredential; credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; } } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; } // 对于其他的challenges直接使用默认的验证方案 completionHandler(disposition, credential); } @end
旧版本Weex SDK实现
以0.7.0
为例子:
网络请求都会通过WXNetworkHttpDnsImpl
实现,所以只需要在WXNetworkHttpDnsImpl
中植入 HTTPDNS 逻辑即可,具体逻辑可以参考如下代码:
#import "WXNetworkDefaultImpl.h"
@interface WXNetworkCallbackInfo : NSObject
@property (nonatomic, copy) void(^sendDataCallback)(int64_t, int64_t);
@property (nonatomic, copy) void(^responseCallback)(NSURLResponse *);
@property (nonatomic, copy) void(^receiveDataCallback)(NSData *);
@property (nonatomic, strong) NSMutableData *data;
@property (nonatomic, copy) void(^compeletionCallback)(NSData *, NSError *);
@end
@implementation WXNetworkCallbackInfo
@end
@implementation WXNetworkDefaultImpl
{
NSMutableDictionary *_callbacks;
NSURLSession *_session;
}
- (id)sendRequest:(NSURLRequest *)request withSendingData:(void (^)(int64_t, int64_t))sendDataCallback
withResponse:(void (^)(NSURLResponse *))responseCallback
withReceiveData:(void (^)(NSData *))receiveDataCallback
withCompeletion:(void (^)(NSData *, NSError *))compeletionCallback
{
WXNetworkCallbackInfo *info = [WXNetworkCallbackInfo new];
info.sendDataCallback = sendDataCallback;
info.responseCallback = responseCallback;
info.receiveDataCallback = receiveDataCallback;
info.compeletionCallback = compeletionCallback;
if (!_session) {
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
}
NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
if (!_callbacks) {
_callbacks = [NSMutableDictionary dictionary];
}
[_callbacks setObject:info forKey:task];
[task resume];
return task;
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
if (info.sendDataCallback) {
info.sendDataCallback(totalBytesSent, totalBytesExpectedToSend);
}
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
if (info.responseCallback) {
info.responseCallback(response);
}
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveData:(NSData *)data
{
WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
if (info.receiveDataCallback) {
info.receiveDataCallback(data);
}
NSMutableData *mutableData = info.data;
if (!mutableData) {
mutableData = [NSMutableData new];
info.data = mutableData;
}
[mutableData appendData:data];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
if (info.compeletionCallback) {
info.compeletionCallback(info.data, error);
}
[_callbacks removeObjectForKey:task];
}
@end
<image>网络请求+HTTPDNS
WEEX
并没有提供默认的图片适配器实现,所以用户须实现后才能完成图片请求逻辑,具体步骤分为以下几步:
自定义图片请求适配器,实现
IWXImgLoaderAdapter
接口#import "WXImgLoaderProtocol.h" @interface WXImgLoaderDefaultImpl : NSObject<WXImgLoaderProtocol, WXModuleProtocol> @end
在
WEEX
初始化时注册该图片适配器:[WXSDKEngine registerHandler:[WXImgLoaderDefaultImpl new] withProtocol:@protocol(WXImgLoaderProtocol)];
所以同
WXNetworkHttpDnsImpl
一样,我们只需在WXNetworkHttpDnsImpl
植入HTTPDNS
逻辑即可。