Integrate HTTPDNS with AFNetworking on iOS for HTTP and HTTPS scenarios, including SNI support.
1. Overview
AFNetworking is a widely used networking framework for iOS. This guide covers integrating HTTPDNS into an AFNetworking-based project.
HTTPDNS basics and iOS-specific technical challenges are covered in Use HTTPDNS in native iOS scenarios.
2. Integration for standard HTTP and non-SNI HTTPS scenarios
Applies to standard HTTP and HTTPS scenarios without Server Name Indication (SNI).
2.1 Create an AFHTTPSessionManager
+ (AFHTTPSessionManager *)sharedAfnManager {
static AFHTTPSessionManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [AFHTTPSessionManager manager];
// Configure the security policy.
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
securityPolicy.allowInvalidCertificates = NO;
securityPolicy.validatesDomainName = YES;
manager.securityPolicy = securityPolicy;
// Configure the serializer.
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:
@"application/json", @"text/json", @"text/javascript", @"text/plain", nil];
manager.requestSerializer.timeoutInterval = 10.0f;
});
return manager;
}
2.2 Perform HTTPDNS domain name resolution
+ (NSString *)resolveAvailableIp:(NSString *)host {
HttpDnsService *httpDnsService = [HttpDnsService sharedInstance];
HttpdnsResult *result = [httpDnsService resolveHostSyncNonBlocking:host
byIpType:HttpdnsQueryIPTypeAuto];
if (!result) return nil;
if (result.hasIpv4Address) {
return result.firstIpv4Address;
} else if (result.hasIpv6Address) {
return [NSString stringWithFormat:@"[%@]", result.firstIpv6Address];
}
return nil;
}
2.3 Send a network request and handle certificate validation
+ (void)httpDnsQueryWithURL:(NSString *)originalUrl
completionHandler:(void(^)(NSString *message))completionHandler {
NSURL *url = [NSURL URLWithString:originalUrl];
NSString *resolvedIpAddress = [self resolveAvailableIp:url.host];
NSString *requestUrl = originalUrl;
if (resolvedIpAddress) {
// Replace the domain name with the resolved IP address.
requestUrl = [originalUrl stringByReplacingOccurrencesOfString:url.host
withString:resolvedIpAddress];
NSLog(@"HTTPDNS resolution successful. Domain name: %@, IP address: %@", url.host, resolvedIpAddress);
} else {
NSLog(@"HTTPDNS resolution failed. Using original URL: %@", url.host);
}
AFHTTPSessionManager *manager = [self sharedAfnManager];
// Important: Set the Host header to ensure the server correctly identifies the domain name.
[manager.requestSerializer setValue:url.host forHTTPHeaderField:@"host"];
// Important: Configure HTTPS certificate validation. Use the original domain name for validation.
[manager setSessionDidReceiveAuthenticationChallengeBlock:
^NSURLSessionAuthChallengeDisposition(NSURLSession *session,
NSURLAuthenticationChallenge *challenge,
NSURLCredential **credential) {
if ([challenge.protectionSpace.authenticationMethod
isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust
forDomain:url.host]) {
*credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
return NSURLSessionAuthChallengeUseCredential;
}
}
return NSURLSessionAuthChallengePerformDefaultHandling;
}];
// Send the request.
[manager GET:requestUrl parameters:nil headers:nil progress:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
NSData *data = [[NSData alloc] initWithData:responseObject];
NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (completionHandler) {
completionHandler([NSString stringWithFormat:@"Request successful: %@", dataStr]);
}
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
if (completionHandler) {
completionHandler([NSString stringWithFormat:@"Request failed: %@", error.localizedDescription]);
}
}];
}
+ (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
// Create a certificate validation policy.
NSMutableArray *policies = [NSMutableArray array];
if (domain) {
[policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)];
} else {
[policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()];
}
// Attach the validation policy to the server certificate.
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef) policies);
// Evaluate the certificate trust.
SecTrustResultType result;
SecTrustEvaluate(serverTrust, &result);
return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
}
3. Integration for HTTPS with SNI scenarios
Applies to scenarios that require SNI, such as CDN or shared-IP deployments.
3.1 Configure an AFHTTPSessionManager that supports SNI
+ (AFHTTPSessionManager *)sharedAfnManagerWithSNI {
static AFHTTPSessionManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// Important: Register a custom NSURLProtocol to handle SNI.
NSMutableArray *protocolsArray = [NSMutableArray arrayWithArray:configuration.protocolClasses];
[protocolsArray insertObject:[HttpDnsNSURLProtocolImpl class] atIndex:0];
[configuration setProtocolClasses:protocolsArray];
manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
// Configure the security policy.
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
securityPolicy.allowInvalidCertificates = NO;
securityPolicy.validatesDomainName = YES;
manager.securityPolicy = securityPolicy;
// Configure the serializer.
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:
@"application/json", @"text/json", @"text/javascript", @"text/plain", nil];
manager.requestSerializer.timeoutInterval = 10.0f;
});
return manager;
}
3.2 Perform HTTPDNS domain name resolution
+ (NSString *)resolveAvailableIp:(NSString *)host {
HttpDnsService *httpDnsService = [HttpDnsService sharedInstance];
HttpdnsResult *result = [httpDnsService resolveHostSyncNonBlocking:host
byIpType:HttpdnsQueryIPTypeAuto];
if (!result) return nil;
if (result.hasIpv4Address) {
return result.firstIpv4Address;
} else if (result.hasIpv6Address) {
return [NSString stringWithFormat:@"[%@]", result.firstIpv6Address];
}
return nil;
}
3.3 Send a network request in an SNI scenario
+ (void)httpDnsQueryWithSNIURL:(NSString *)originalUrl
completionHandler:(void(^)(NSString *message))completionHandler {
NSURL *url = [NSURL URLWithString:originalUrl];
NSString *resolvedIpAddress = [self resolveAvailableIp:url.host];
NSString *requestUrl = originalUrl;
if (resolvedIpAddress) {
requestUrl = [originalUrl stringByReplacingOccurrencesOfString:url.host
withString:resolvedIpAddress];
NSLog(@"HTTPDNS resolution successful. Domain name: %@, IP address: %@", url.host, resolvedIpAddress);
} else {
NSLog(@"HTTPDNS resolution failed. Using original URL: %@", url.host);
}
AFHTTPSessionManager *manager = [self sharedAfnManagerWithSNI];
// Set the Host header.
[manager.requestSerializer setValue:url.host forHTTPHeaderField:@"host"];
// Note: Because a custom NSURLProtocol is used, SNI and certificate validation are handled at the protocol layer.
// Send the request directly.
[manager GET:requestUrl parameters:nil headers:nil progress:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
NSData *data = [[NSData alloc] initWithData:responseObject];
NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (completionHandler) {
completionHandler([NSString stringWithFormat:@"Request successful: %@", dataStr]);
}
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
if (completionHandler) {
completionHandler([NSString stringWithFormat:@"Request failed: %@", error.localizedDescription]);
}
}];
}
Note: In SNI scenarios, the NSURLProtocol layer handles certificate validation and domain name resolution automatically. No separate callback is required.
A sample implementation is available in HttpDnsNSURLProtocolImpl.m from the httpdns_ios_demo project.
4. Examples
4.1 Basic HTTPS request
// Standard HTTP request (non-SNI scenario)
[AFNHttpsScenario httpDnsQueryWithURL:@"http://example.com/api/data"
completionHandler:^(NSString *message) {
NSLog(@"Request result: %@", message);
}];
4.2 Request in an SNI scenario
// Request in an SNI scenario
[AFNHttpsWithSNIScenario httpDnsQueryWithSNIURL:@"https://example.com/api/data"
completionHandler:^(NSString *message) {
NSLog(@"Request result: %@", message);
}];
5. Summary
Core integration steps:
-
Initialize AFHTTPSessionManager: Configure basic network parameters.
-
Perform HTTPDNS domain name resolution: Replace the domain name with a resolved IP address.
-
Send the network request: Set the Host header and handle HTTPS certificate validation.
Key points
-
Host header: Ensures the server identifies the correct domain.
-
Certificate validation: Use the original domain name, not the IP address, for certificate validation.
-
SNI handling: Use NSURLProtocol for automatic handling in complex scenarios.
-
Fallback policy: Fall back to the system DNS if HTTPDNS resolution fails.
Complete sample code: HTTPDNS iOS Demo.