QNHttpSingleRequest.m 11 KB

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