OSSHttpdns.m 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. //
  2. // OSSHttpdns.m
  3. // AliyunOSSiOS
  4. //
  5. // Created by zhouzhuo on 5/1/16.
  6. // Copyright © 2016 zhouzhuo. All rights reserved.
  7. //
  8. #import "OSSLog.h"
  9. #import "OSSHttpdns.h"
  10. #import "OSSIPv6Adapter.h"
  11. NSString * const OSS_HTTPDNS_SERVER_IP = @"203.107.1.1";
  12. NSString * const OSS_HTTPDNS_SERVER_PORT = @"80";
  13. NSString * const ACCOUNT_ID = @"181345";
  14. NSTimeInterval const MAX_ENDURABLE_EXPIRED_TIME_IN_SECOND = 60; // The DNS entry's expiration time in seconds. After it expires, the entry is invalid.
  15. NSTimeInterval const PRERESOLVE_IN_ADVANCE_IN_SECOND = 10; // Once the remaining valid time of an DNS entry is less than this number, issue a DNS request to prefetch the data.
  16. @interface IpObject : NSObject
  17. @property (nonatomic, copy) NSString * ip;
  18. @property (nonatomic, assign) NSTimeInterval expiredTime;
  19. @end
  20. @implementation IpObject
  21. @end
  22. @implementation OSSHttpdns {
  23. NSMutableDictionary * gHostIpMap;
  24. NSMutableSet * penddingSet;
  25. }
  26. + (instancetype)sharedInstance {
  27. static OSSHttpdns * sharedInstance = nil;
  28. static dispatch_once_t onceToken;
  29. dispatch_once(&onceToken, ^{
  30. sharedInstance = [OSSHttpdns new];
  31. });
  32. return sharedInstance;
  33. }
  34. - (instancetype)init {
  35. if (self = [super init]) {
  36. gHostIpMap = [NSMutableDictionary new];
  37. penddingSet = [NSMutableSet new];
  38. }
  39. return self;
  40. }
  41. /**
  42. * OSS SDK specific
  43. *
  44. * @param host it needs strictly follow the domain's format, such as oss-cn-hangzhou.aliyuncs.com
  45. *
  46. * @return an ip in the ip list of the resolved host.
  47. */
  48. - (NSString *)asynGetIpByHost:(NSString *)host {
  49. IpObject * ipObject = [gHostIpMap objectForKey:host];
  50. if (!ipObject) {
  51. // if the host is not resolved, asynchronously resolve it and return nil
  52. [self resolveHost:host];
  53. return nil;
  54. } else if ([[NSDate date] timeIntervalSince1970] - ipObject.expiredTime > MAX_ENDURABLE_EXPIRED_TIME_IN_SECOND) {
  55. // If the entry is expired, asynchronously resolve it and return nil.
  56. [self resolveHost:host];
  57. return nil;
  58. } else if (ipObject.expiredTime -[[NSDate date] timeIntervalSince1970] < PRERESOLVE_IN_ADVANCE_IN_SECOND) {
  59. // If the entry is about to expire, asynchronously resolve it and return the current value.
  60. [self resolveHost:host];
  61. return ipObject.ip;
  62. } else {
  63. // returns the current result.
  64. return ipObject.ip;
  65. }
  66. }
  67. /**
  68. * resolve the host asynchronously
  69. * If the host is being resolved, the call will be skipped.
  70. *
  71. * @param host the host to resolve
  72. */
  73. - (void)resolveHost:(NSString *)host {
  74. @synchronized (self) {
  75. if ([penddingSet containsObject:host]) {
  76. return;
  77. } else {
  78. [penddingSet addObject:host];
  79. }
  80. }
  81. NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@/%@/d?host=%@", [[OSSIPv6Adapter getInstance] handleIpv4Address:OSS_HTTPDNS_SERVER_IP], ACCOUNT_ID, host]];
  82. NSURLSession * session = [NSURLSession sharedSession];
  83. NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  84. IpObject * ipObject = nil;
  85. NSUInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
  86. if (statusCode != 200) {
  87. OSSLogError(@"Httpdns resolve host: %@ failed, responseCode: %lu", host, (unsigned long)statusCode);
  88. } else {
  89. NSError *error = nil;
  90. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
  91. NSTimeInterval expiredTime = [[NSDate new] timeIntervalSince1970] + [[json objectForKey:@"ttl"] longLongValue];
  92. NSArray *ips = [json objectForKey:@"ips"];
  93. if (ips == nil || [ips count] == 0) {
  94. OSSLogError(@"Httpdns resolve host: %@ failed, ip list empty.", host);
  95. } else {
  96. NSString * ip = ips[0];
  97. ipObject = [IpObject new];
  98. ipObject.expiredTime = expiredTime;
  99. ipObject.ip = ip;
  100. OSSLogDebug(@"Httpdns resolve host: %@ success, ip: %@, expiredTime: %lf", host, ipObject.ip, ipObject.expiredTime);
  101. }
  102. }
  103. @synchronized (self) {
  104. if (ipObject) {
  105. gHostIpMap[host] = ipObject;
  106. }
  107. [penddingSet removeObject:host];
  108. }
  109. }];
  110. [dataTask resume];
  111. }
  112. @end