QNConnectChecker.m 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. //
  2. // QNConnectChecker.m
  3. // QiniuSDK_Mac
  4. //
  5. // Created by yangsen on 2021/1/8.
  6. // Copyright © 2021 Qiniu. All rights reserved.
  7. //
  8. #import "QNDefine.h"
  9. #import "QNLogUtil.h"
  10. #import "QNConfiguration.h"
  11. #import "QNSingleFlight.h"
  12. #import "QNConnectChecker.h"
  13. #import "QNUploadSystemClient.h"
  14. @interface QNConnectChecker()
  15. @end
  16. @implementation QNConnectChecker
  17. + (QNSingleFlight *)singleFlight {
  18. static QNSingleFlight *singleFlight = nil;
  19. static dispatch_once_t onceToken;
  20. dispatch_once(&onceToken, ^{
  21. singleFlight = [[QNSingleFlight alloc] init];
  22. });
  23. return singleFlight;
  24. }
  25. + (dispatch_queue_t)checkQueue {
  26. static dispatch_queue_t queue;
  27. static dispatch_once_t onceToken;
  28. dispatch_once(&onceToken, ^{
  29. queue = dispatch_queue_create("com.qiniu.NetworkCheckQueue", DISPATCH_QUEUE_CONCURRENT);
  30. });
  31. return queue;
  32. }
  33. + (BOOL)isConnected:(QNUploadSingleRequestMetrics *)metrics {
  34. return metrics && ((NSHTTPURLResponse *)metrics.response).statusCode > 99;
  35. }
  36. + (QNUploadSingleRequestMetrics *)check {
  37. __block QNUploadSingleRequestMetrics *metrics = nil;
  38. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  39. [self check:^(QNUploadSingleRequestMetrics *metricsP) {
  40. metrics = metricsP;
  41. dispatch_semaphore_signal(semaphore);
  42. }];
  43. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  44. return metrics;
  45. }
  46. + (void)check:(void (^)(QNUploadSingleRequestMetrics *))complete {
  47. QNSingleFlight *singleFlight = [self singleFlight];
  48. kQNWeakSelf;
  49. [singleFlight perform:@"connect_check" action:^(QNSingleFlightComplete _Nonnull singleFlightComplete) {
  50. kQNStrongSelf;
  51. [self checkAllHosts:^(QNUploadSingleRequestMetrics *metrics) {
  52. singleFlightComplete(metrics, nil);
  53. }];
  54. } complete:^(id _Nullable value, NSError * _Nullable error) {
  55. if (complete) {
  56. complete(value);
  57. }
  58. }];
  59. }
  60. + (void)checkAllHosts:(void (^)(QNUploadSingleRequestMetrics *metrics))complete {
  61. __block int completeCount = 0;
  62. __block BOOL isCompleted = false;
  63. kQNWeakSelf;
  64. NSArray *allHosts = [kQNGlobalConfiguration.connectCheckURLStrings copy];
  65. if (allHosts.count == 0) {
  66. QNUploadSingleRequestMetrics *metrics = [QNUploadSingleRequestMetrics emptyMetrics];
  67. [metrics start];
  68. [metrics end];
  69. metrics.error = [NSError errorWithDomain:@"com.qiniu.NetworkCheck" code:NSURLErrorUnsupportedURL userInfo:@{@"user_info":@"check host is empty"}];
  70. complete(metrics);
  71. return;
  72. }
  73. for (NSString *host in allHosts) {
  74. [self checkHost:host complete:^(QNUploadSingleRequestMetrics *metrics) {
  75. kQNStrongSelf;
  76. BOOL isHostConnected = [self isConnected:metrics];
  77. @synchronized (self) {
  78. completeCount += 1;
  79. }
  80. if (isHostConnected || completeCount == allHosts.count) {
  81. @synchronized (self) {
  82. if (isCompleted) {
  83. QNLogInfo(@"== check all hosts has completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
  84. return;
  85. } else {
  86. QNLogInfo(@"== check all hosts completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
  87. isCompleted = true;
  88. }
  89. }
  90. complete(metrics);
  91. } else {
  92. QNLogInfo(@"== check all hosts not completed totalCount:%d completeCount:%d", allHosts.count, completeCount);
  93. }
  94. }];
  95. }
  96. }
  97. + (void)checkHost:(NSString *)host complete:(void (^)(QNUploadSingleRequestMetrics *metrics))complete {
  98. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  99. request.URL = [NSURL URLWithString:host];
  100. request.HTTPMethod = @"HEAD";
  101. request.timeoutInterval = kQNGlobalConfiguration.connectCheckTimeout;
  102. __block BOOL hasCallback = false;
  103. QNUploadSingleRequestMetrics *timeoutMetric = [QNUploadSingleRequestMetrics emptyMetrics];
  104. [timeoutMetric start];
  105. QNUploadSystemClient *client = [[QNUploadSystemClient alloc] init];
  106. [client request:request server:nil connectionProxy:nil progress:nil complete:^(NSURLResponse *response, QNUploadSingleRequestMetrics * metrics, NSData * _Nullable data, NSError * error) {
  107. @synchronized (self) {
  108. if (hasCallback) {
  109. return;
  110. }
  111. hasCallback = true;
  112. }
  113. QNLogInfo(@"== checkHost:%@ responseInfo:%@", host, response);
  114. complete(metrics);
  115. }];
  116. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * kQNGlobalConfiguration.connectCheckTimeout), [self checkQueue], ^{
  117. @synchronized (self) {
  118. if (hasCallback) {
  119. return;
  120. }
  121. hasCallback = true;
  122. }
  123. [client cancel];
  124. [timeoutMetric end];
  125. timeoutMetric.error = [NSError errorWithDomain:@"com.qiniu.NetworkCheck" code:NSURLErrorTimedOut userInfo:nil];
  126. complete(timeoutMetric);
  127. });
  128. }
  129. @end