All Products
Search
Document Center

HTTPDNS:Praktik terbaik untuk menggunakan HTTPDNS dengan AFNetworking di iOS

Last Updated:Nov 10, 2025

Dokumen ini menjelaskan cara mengintegrasikan HTTPDNS dengan AFNetworking pada klien iOS.

1. Ikhtisar

AFNetworking adalah kerangka kerja jaringan populer untuk pengembangan iOS yang menyediakan API sederhana dan fitur andal. Dokumen ini menjelaskan cara mengintegrasikan HTTPDNS ke dalam proyek yang menggunakan AFNetworking.

Untuk informasi dasar mengenai HTTPDNS dan tantangan teknis penggunaannya di iOS, lihat Gunakan HTTPDNS dalam skenario iOS native.

2. Integrasi untuk skenario HTTP standar dan HTTPS non-SNI

Metode ini berlaku untuk skenario HTTP standar dan skenario HTTPS yang tidak memerlukan Server Name Indication (SNI).

2.1 Buat AFHTTPSessionManager

+ (AFHTTPSessionManager *)sharedAfnManager {
    static AFHTTPSessionManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [AFHTTPSessionManager manager];
        
        // Konfigurasikan kebijakan keamanan.
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
        securityPolicy.allowInvalidCertificates = NO;
        securityPolicy.validatesDomainName = YES;
        manager.securityPolicy = securityPolicy;
        
        // Konfigurasikan serializer.
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects: 
            @"application/json", @"text/json", @"text/javascript", @"text/plain", nil];
        manager.requestSerializer.timeoutInterval = 10.0f;
    });
    return manager;
}

2.2 Lakukan resolusi nama domain HTTPDNS

+ (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 Kirim permintaan jaringan dan tangani validasi sertifikat

+ (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) {
        // Ganti nama domain dengan alamat IP hasil resolusi.
        requestUrl = [originalUrl stringByReplacingOccurrencesOfString:url.host 
                                                           withString:resolvedIpAddress];
        NSLog(@"Resolusi HTTPDNS berhasil. Nama domain: %@, Alamat IP: %@", url.host, resolvedIpAddress);
    } else {
        NSLog(@"Resolusi HTTPDNS gagal. Menggunakan URL asli: %@", url.host);
    }

    AFHTTPSessionManager *manager = [self sharedAfnManager];
    
    // Penting: Tetapkan header Host untuk memastikan server mengenali nama domain dengan benar.
    [manager.requestSerializer setValue:url.host forHTTPHeaderField:@"host"];
    
    // Penting: Konfigurasikan validasi sertifikat HTTPS. Gunakan nama domain asli untuk validasi.
    [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;
    }];
    
    // Kirim permintaan.
    [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:@"Permintaan berhasil: %@", dataStr]);
             }
         } 
         failure:^(NSURLSessionDataTask *task, NSError *error) {
             if (completionHandler) {
                 completionHandler([NSString stringWithFormat:@"Permintaan gagal: %@", error.localizedDescription]);
             }
         }];
}

+ (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
    // Buat kebijakan validasi sertifikat.
    NSMutableArray *policies = [NSMutableArray array];
    if (domain) {
        [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)];
    } else {
        [policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()];
    }
    
    // Lampirkan kebijakan validasi ke sertifikat server.
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef) policies);
    
    // Evaluasi kepercayaan sertifikat.
    SecTrustResultType result;
    SecTrustEvaluate(serverTrust, &result);
    
    return (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
}

3. Integrasi untuk skenario HTTPS dengan SNI

Metode ini berlaku untuk skenario yang memerlukan dukungan SNI, seperti penggunaan CDN atau berbagi alamat IP di antara beberapa nama domain.

3.1 Konfigurasikan AFHTTPSessionManager yang mendukung SNI

+ (AFHTTPSessionManager *)sharedAfnManagerWithSNI {
    static AFHTTPSessionManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        
        // Penting: Daftarkan NSURLProtocol kustom untuk menangani SNI.
        NSMutableArray *protocolsArray = [NSMutableArray arrayWithArray:configuration.protocolClasses];
        [protocolsArray insertObject:[HttpDnsNSURLProtocolImpl class] atIndex:0];
        [configuration setProtocolClasses:protocolsArray];
        
        manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
        
        // Konfigurasikan kebijakan keamanan.
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
        securityPolicy.allowInvalidCertificates = NO;
        securityPolicy.validatesDomainName = YES;
        manager.securityPolicy = securityPolicy;
        
        // Konfigurasikan serializer.
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects: 
            @"application/json", @"text/json", @"text/javascript", @"text/plain", nil];
        manager.requestSerializer.timeoutInterval = 10.0f;
    });
    return manager;
}

3.2 Lakukan resolusi nama domain HTTPDNS

+ (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 Kirim permintaan jaringan dalam skenario SNI

+ (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(@"Resolusi HTTPDNS berhasil. Nama domain: %@, Alamat IP: %@", url.host, resolvedIpAddress);
    } else {
        NSLog(@"Resolusi HTTPDNS gagal. Menggunakan URL asli: %@", url.host);
    }

    AFHTTPSessionManager *manager = [self sharedAfnManagerWithSNI];
    
    // Tetapkan header Host.
    [manager.requestSerializer setValue:url.host forHTTPHeaderField:@"host"];
    
    // Catatan: Karena NSURLProtocol kustom digunakan, SNI dan validasi sertifikat ditangani di lapisan protokol.
    // Kirim permintaan secara langsung.
    [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:@"Permintaan berhasil: %@", dataStr]);
             }
         } 
         failure:^(NSURLSessionDataTask *task, NSError *error) {
             if (completionHandler) {
                 completionHandler([NSString stringWithFormat:@"Permintaan gagal: %@", error.localizedDescription]);
             }
         }];
}

Catatan: Dalam skenario SNI, validasi sertifikat dan penanganan nama domain diproses secara otomatis di lapisan NSURLProtocol. Anda tidak perlu mengonfigurasi callback validasi sertifikat secara terpisah.

Untuk contoh implementasi, lihat file HttpDnsNSURLProtocolImpl.m dalam httpdns_ios_demo Alibaba Cloud. Anda dapat memodifikasi atau menggunakan kembali implementasi ini sesuai kebutuhan.

4. Contoh

4.1 Permintaan HTTPS dasar

// Permintaan HTTP standar (skenario non-SNI)
[AFNHttpsScenario httpDnsQueryWithURL:@"http://example.com/api/data" 
                    completionHandler:^(NSString *message) {
    NSLog(@"Hasil permintaan: %@", message);
}];

4.2 Permintaan dalam skenario SNI

// Permintaan dalam skenario SNI
[AFNHttpsWithSNIScenario httpDnsQueryWithSNIURL:@"https://example.com/api/data" 
                               completionHandler:^(NSString *message) {
    NSLog(@"Hasil permintaan: %@", message);
}];

5. Ringkasan

Langkah-langkah inti untuk mengintegrasikan HTTPDNS dengan AFNetworking adalah:

  1. Inisialisasi AFHTTPSessionManager: Konfigurasikan parameter jaringan dasar.

  2. Lakukan resolusi nama domain HTTPDNS: Dapatkan alamat IP untuk menggantikan nama domain.

  3. Kirim permintaan jaringan: Tetapkan header Host dan tangani validasi sertifikat HTTPS.

Poin-poin penting

  • Header Host: Pastikan server dapat mengenali nama domain yang diminta dengan benar.

  • Validasi sertifikat: Gunakan nama domain asli, bukan alamat IP, untuk validasi sertifikat.

  • Penanganan SNI: Gunakan NSURLProtocol untuk penanganan otomatis dalam skenario kompleks.

  • Kebijakan fallback: Kembali ke DNS sistem jika resolusi HTTPDNS gagal.

Untuk kode contoh lengkap, lihat HTTPDNS iOS Demo.