QNHttpSingleRequest.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. //
  2. // QNHttpRequest+SingleRequestRetry.m
  3. // QiniuSDK
  4. //
  5. // Created by yangsen on 2020/4/29.
  6. // Copyright © 2020 Qiniu. All rights reserved.
  7. //
  8. #import "QNDefine.h"
  9. #import "QNAsyncRun.h"
  10. #import "QNVersion.h"
  11. #import "QNUtils.h"
  12. #import "QNLogUtil.h"
  13. #import "QNHttpSingleRequest.h"
  14. #import "QNConfiguration.h"
  15. #import "QNUploadOption.h"
  16. #import "QNUpToken.h"
  17. #import "QNResponseInfo.h"
  18. #import "QNNetworkStatusManager.h"
  19. #import "QNRequestClient.h"
  20. #import "QNUploadRequestState.h"
  21. #import "QNConnectChecker.h"
  22. #import "QNDnsPrefetch.h"
  23. #import "QNReportItem.h"
  24. #import "QNUploadSystemClient.h"
  25. #import "NSURLRequest+QNRequest.h"
  26. @interface QNHttpSingleRequest()
  27. @property(nonatomic, assign)int currentRetryTime;
  28. @property(nonatomic, strong)QNConfiguration *config;
  29. @property(nonatomic, strong)QNUploadOption *uploadOption;
  30. @property(nonatomic, strong)QNUpToken *token;
  31. @property(nonatomic, strong)QNUploadRequestInfo *requestInfo;
  32. @property(nonatomic, strong)QNUploadRequestState *requestState;
  33. @property(nonatomic, strong)NSMutableArray <QNUploadSingleRequestMetrics *> *requestMetricsList;
  34. @property(nonatomic, strong)id <QNRequestClient> client;
  35. @end
  36. @implementation QNHttpSingleRequest
  37. - (instancetype)initWithConfig:(QNConfiguration *)config
  38. uploadOption:(QNUploadOption *)uploadOption
  39. token:(QNUpToken *)token
  40. requestInfo:(QNUploadRequestInfo *)requestInfo
  41. requestState:(QNUploadRequestState *)requestState{
  42. if (self = [super init]) {
  43. _config = config;
  44. _uploadOption = uploadOption;
  45. _token = token;
  46. _requestInfo = requestInfo;
  47. _requestState = requestState;
  48. _currentRetryTime = 0;
  49. }
  50. return self;
  51. }
  52. - (void)request:(NSURLRequest *)request
  53. server:(id <QNUploadServer>)server
  54. shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
  55. progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
  56. complete:(QNSingleRequestCompleteHandler)complete{
  57. _currentRetryTime = 0;
  58. _requestMetricsList = [NSMutableArray array];
  59. [self retryRequest:request server:server shouldRetry:shouldRetry progress:progress complete:complete];
  60. }
  61. - (void)retryRequest:(NSURLRequest *)request
  62. server:(id <QNUploadServer>)server
  63. shouldRetry:(BOOL(^)(QNResponseInfo *responseInfo, NSDictionary *response))shouldRetry
  64. progress:(void(^)(long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
  65. complete:(QNSingleRequestCompleteHandler)complete{
  66. if (kQNIsHttp3(server.httpVersion)) {
  67. self.client = [[QNUploadSystemClient alloc] init];
  68. } else {
  69. self.client = [[QNUploadSystemClient alloc] init];
  70. }
  71. kQNWeakSelf;
  72. BOOL (^checkCancelHandler)(void) = ^{
  73. kQNStrongSelf;
  74. BOOL isCancelled = self.requestState.isUserCancel;
  75. if (!isCancelled && self.uploadOption.cancellationSignal) {
  76. isCancelled = self.uploadOption.cancellationSignal();
  77. }
  78. return isCancelled;
  79. };
  80. QNLogInfo(@"key:%@ retry:%d url:%@", self.requestInfo.key, self.currentRetryTime, request.URL);
  81. [self.client request:request connectionProxy:self.config.proxy progress:^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
  82. kQNStrongSelf;
  83. if (checkCancelHandler()) {
  84. self.requestState.isUserCancel = YES;
  85. [self.client cancel];
  86. } else if (progress) {
  87. progress(totalBytesWritten, totalBytesExpectedToWrite);
  88. }
  89. } complete:^(NSURLResponse *response, QNUploadSingleRequestMetrics *metrics, NSData * responseData, NSError * error) {
  90. kQNStrongSelf;
  91. if (metrics) {
  92. [self.requestMetricsList addObject:metrics];
  93. }
  94. QNResponseInfo *responseInfo = nil;
  95. if (checkCancelHandler()) {
  96. responseInfo = [QNResponseInfo cancelResponse];
  97. [self complete:responseInfo server:server response:nil requestMetrics:metrics complete:complete];
  98. return;
  99. }
  100. NSDictionary *responseDic = nil;
  101. if (responseData) {
  102. responseDic = [NSJSONSerialization JSONObjectWithData:responseData
  103. options:NSJSONReadingMutableLeaves
  104. error:nil];
  105. }
  106. responseInfo = [[QNResponseInfo alloc] initWithResponseInfoHost:request.qn_domain
  107. response:(NSHTTPURLResponse *)response
  108. body:responseData
  109. error:error];
  110. if ([self shouldCheckConnect:responseInfo]) {
  111. QNUploadSingleRequestMetrics *connectCheckMetrics = [QNConnectChecker check];
  112. metrics.connectCheckMetrics = connectCheckMetrics;
  113. if (![QNConnectChecker isConnected:connectCheckMetrics]) {
  114. NSString *message = [NSString stringWithFormat:@"check origin statusCode:%d error:%@", responseInfo.statusCode, responseInfo.error];
  115. responseInfo = [QNResponseInfo errorResponseInfo:NSURLErrorNotConnectedToInternet errorDesc:message];
  116. }
  117. }
  118. QNLogInfo(@"key:%@ response:%@", self.requestInfo.key, responseInfo);
  119. if (shouldRetry(responseInfo, responseDic)
  120. && self.currentRetryTime < self.config.retryMax
  121. && responseInfo.couldHostRetry) {
  122. self.currentRetryTime += 1;
  123. QNAsyncRunAfter(self.config.retryInterval, kQNBackgroundQueue, ^{
  124. [self retryRequest:request server:server shouldRetry:shouldRetry progress:progress complete:complete];
  125. });
  126. } else {
  127. [self complete:responseInfo server:server response:responseDic requestMetrics:metrics complete:complete];
  128. }
  129. }];
  130. }
  131. - (BOOL)shouldCheckConnect:(QNResponseInfo *)responseInfo {
  132. return responseInfo.statusCode == kQNNetworkError ||
  133. responseInfo.statusCode == -1001 /* NSURLErrorTimedOut */ ||
  134. responseInfo.statusCode == -1003 /* NSURLErrorCannotFindHost */ ||
  135. responseInfo.statusCode == -1004 /* NSURLErrorCannotConnectToHost */ ||
  136. responseInfo.statusCode == -1005 /* NSURLErrorNetworkConnectionLost */ ||
  137. responseInfo.statusCode == -1006 /* NSURLErrorDNSLookupFailed */ ||
  138. responseInfo.statusCode == -1009 /* NSURLErrorNotConnectedToInternet */;
  139. }
  140. - (void)complete:(QNResponseInfo *)responseInfo
  141. server:(id<QNUploadServer>)server
  142. response:(NSDictionary *)response
  143. requestMetrics:(QNUploadSingleRequestMetrics *)requestMetrics
  144. complete:(QNSingleRequestCompleteHandler)complete {
  145. [self updateHostNetworkStatus:responseInfo server:server requestMetrics:requestMetrics];
  146. [self reportRequest:responseInfo server:server requestMetrics:requestMetrics];
  147. if (complete) {
  148. complete(responseInfo, [self.requestMetricsList copy], response);
  149. }
  150. }
  151. //MARK:-- 统计网络状态
  152. - (void)updateHostNetworkStatus:(QNResponseInfo *)responseInfo
  153. server:(id <QNUploadServer>)server
  154. requestMetrics:(QNUploadSingleRequestMetrics *)requestMetrics{
  155. long long byte = requestMetrics.bytesSend.longLongValue;
  156. if (requestMetrics.startDate && requestMetrics.endDate && byte >= 1024 * 1024) {
  157. double second = [requestMetrics.endDate timeIntervalSinceDate:requestMetrics.startDate];
  158. if (second > 0) {
  159. int speed = (int)(byte / second);
  160. NSString *type = [QNNetworkStatusManager getNetworkStatusType:server.host ip:server.ip];
  161. [kQNNetworkStatusManager updateNetworkStatus:type speed:speed];
  162. }
  163. }
  164. }
  165. //MARK:-- 统计quality日志
  166. - (void)reportRequest:(QNResponseInfo *)info
  167. server:(id <QNUploadServer>)server
  168. requestMetrics:(QNUploadSingleRequestMetrics *)requestMetrics {
  169. if (! [self.requestInfo shouldReportRequestLog]) {
  170. return;
  171. }
  172. QNUploadSingleRequestMetrics *requestMetricsP = requestMetrics ?: [QNUploadSingleRequestMetrics emptyMetrics];
  173. NSInteger currentTimestamp = [QNUtils currentTimestamp];
  174. QNReportItem *item = [QNReportItem item];
  175. [item setReportValue:QNReportLogTypeRequest forKey:QNReportRequestKeyLogType];
  176. [item setReportValue:@(currentTimestamp/1000) forKey:QNReportRequestKeyUpTime];
  177. [item setReportValue:info.requestReportStatusCode forKey:QNReportRequestKeyStatusCode];
  178. [item setReportValue:info.reqId forKey:QNReportRequestKeyRequestId];
  179. [item setReportValue:requestMetricsP.request.qn_domain forKey:QNReportRequestKeyHost];
  180. [item setReportValue:requestMetricsP.remoteAddress forKey:QNReportRequestKeyRemoteIp];
  181. [item setReportValue:requestMetricsP.localPort forKey:QNReportRequestKeyPort];
  182. [item setReportValue:self.requestInfo.bucket forKey:QNReportRequestKeyTargetBucket];
  183. [item setReportValue:self.requestInfo.key forKey:QNReportRequestKeyTargetKey];
  184. [item setReportValue:requestMetricsP.totalElapsedTime forKey:QNReportRequestKeyTotalElapsedTime];
  185. [item setReportValue:requestMetricsP.totalDnsTime forKey:QNReportRequestKeyDnsElapsedTime];
  186. [item setReportValue:requestMetricsP.totalConnectTime forKey:QNReportRequestKeyConnectElapsedTime];
  187. [item setReportValue:requestMetricsP.totalSecureConnectTime forKey:QNReportRequestKeyTLSConnectElapsedTime];
  188. [item setReportValue:requestMetricsP.totalRequestTime forKey:QNReportRequestKeyRequestElapsedTime];
  189. [item setReportValue:requestMetricsP.totalWaitTime forKey:QNReportRequestKeyWaitElapsedTime];
  190. [item setReportValue:requestMetricsP.totalWaitTime forKey:QNReportRequestKeyResponseElapsedTime];
  191. [item setReportValue:requestMetricsP.totalResponseTime forKey:QNReportRequestKeyResponseElapsedTime];
  192. [item setReportValue:self.requestInfo.fileOffset forKey:QNReportRequestKeyFileOffset];
  193. [item setReportValue:requestMetricsP.bytesSend forKey:QNReportRequestKeyBytesSent];
  194. [item setReportValue:requestMetricsP.totalBytes forKey:QNReportRequestKeyBytesTotal];
  195. [item setReportValue:@([QNUtils getCurrentProcessID]) forKey:QNReportRequestKeyPid];
  196. [item setReportValue:@([QNUtils getCurrentThreadID]) forKey:QNReportRequestKeyTid];
  197. [item setReportValue:self.requestInfo.targetRegionId forKey:QNReportRequestKeyTargetRegionId];
  198. [item setReportValue:self.requestInfo.currentRegionId forKey:QNReportRequestKeyCurrentRegionId];
  199. [item setReportValue:info.requestReportErrorType forKey:QNReportRequestKeyErrorType];
  200. NSString *errorDesc = info.requestReportErrorType ? info.message : nil;
  201. [item setReportValue:errorDesc forKey:QNReportRequestKeyErrorDescription];
  202. [item setReportValue:self.requestInfo.requestType forKey:QNReportRequestKeyUpType];
  203. [item setReportValue:[QNUtils systemName] forKey:QNReportRequestKeyOsName];
  204. [item setReportValue:[QNUtils systemVersion] forKey:QNReportRequestKeyOsVersion];
  205. [item setReportValue:[QNUtils sdkLanguage] forKey:QNReportRequestKeySDKName];
  206. [item setReportValue:[QNUtils sdkVersion] forKey:QNReportRequestKeySDKVersion];
  207. [item setReportValue:@([QNUtils currentTimestamp]) forKey:QNReportRequestKeyClientTime];
  208. [item setReportValue:[QNUtils getCurrentNetworkType] forKey:QNReportRequestKeyNetworkType];
  209. [item setReportValue:[QNUtils getCurrentSignalStrength] forKey:QNReportRequestKeySignalStrength];
  210. [item setReportValue:server.source forKey:QNReportRequestKeyPrefetchedDnsSource];
  211. if (server.ipPrefetchedTime) {
  212. NSInteger prefetchTime = currentTimestamp/1000 - [server.ipPrefetchedTime integerValue];
  213. [item setReportValue:@(prefetchTime) forKey:QNReportRequestKeyPrefetchedBefore];
  214. }
  215. [item setReportValue:kQNDnsPrefetch.lastPrefetchedErrorMessage forKey:QNReportRequestKeyPrefetchedErrorMessage];
  216. [item setReportValue:requestMetricsP.httpVersion forKey:QNReportRequestKeyHttpVersion];
  217. if (requestMetricsP.connectCheckMetrics) {
  218. QNUploadSingleRequestMetrics *metrics = requestMetricsP.connectCheckMetrics;
  219. NSString *connectCheckDuration = [NSString stringWithFormat:@"%.2lf", [metrics.totalElapsedTime doubleValue]];
  220. NSString *connectCheckStatusCode = @"";
  221. if (metrics.response) {
  222. connectCheckStatusCode = [NSString stringWithFormat:@"%ld", (long)((NSHTTPURLResponse *)metrics.response).statusCode];
  223. } else if (metrics.error) {
  224. connectCheckStatusCode = [NSString stringWithFormat:@"%ld", metrics.error.code];
  225. }
  226. NSString *networkMeasuring = [NSString stringWithFormat:@"duration:%@ status_code:%@",connectCheckDuration, connectCheckStatusCode];
  227. [item setReportValue:networkMeasuring forKey:QNReportRequestKeyNetworkMeasuring];
  228. }
  229. [kQNReporter reportItem:item token:self.token.token];
  230. }
  231. @end