QNResponseInfo.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. //
  2. // QNResponseInfo.m
  3. // QiniuSDK
  4. //
  5. // Created by bailong on 14/10/2.
  6. // Copyright (c) 2014年 Qiniu. All rights reserved.
  7. //
  8. #import "QNResponseInfo.h"
  9. #import "QNUserAgent.h"
  10. #import "QNUtils.h"
  11. static NSString *kQNErrorDomain = @"qiniu.com";
  12. @interface QNResponseInfo ()
  13. @property (assign) int statusCode;
  14. @property (nonatomic, copy) NSString *message;
  15. @property (nonatomic, copy) NSString *reqId;
  16. @property (nonatomic, copy) NSString *xlog;
  17. @property (nonatomic, copy) NSString *xvia;
  18. @property (nonatomic, copy) NSError *error;
  19. @property (nonatomic, copy) NSString *host;
  20. @property (nonatomic, copy) NSString *id;
  21. @property (assign) UInt64 timeStamp;
  22. @end
  23. @implementation QNResponseInfo
  24. + (instancetype)successResponse{
  25. QNResponseInfo *responseInfo = [[QNResponseInfo alloc] init];
  26. responseInfo.statusCode = 200;
  27. responseInfo.message = @"inter:ok";
  28. responseInfo.xlog = @"inter:xlog";
  29. responseInfo.reqId = @"inter:reqid";
  30. return responseInfo;
  31. }
  32. + (instancetype)cancelResponse {
  33. return [QNResponseInfo errorResponseInfo:kQNRequestCancelled
  34. errorDesc:@"cancelled by user"];
  35. }
  36. + (instancetype)responseInfoWithNetworkError:(NSString *)desc{
  37. return [QNResponseInfo errorResponseInfo:kQNNetworkError
  38. errorDesc:desc];
  39. }
  40. + (instancetype)responseInfoWithInvalidArgument:(NSString *)desc{
  41. return [QNResponseInfo errorResponseInfo:kQNInvalidArgument
  42. errorDesc:desc];
  43. }
  44. + (instancetype)responseInfoWithInvalidToken:(NSString *)desc {
  45. return [QNResponseInfo errorResponseInfo:kQNInvalidToken
  46. errorDesc:desc];
  47. }
  48. + (instancetype)responseInfoWithFileError:(NSError *)error {
  49. return [QNResponseInfo errorResponseInfo:kQNFileError
  50. errorDesc:nil
  51. error:error];
  52. }
  53. + (instancetype)responseInfoOfZeroData:(NSString *)path {
  54. NSString *desc;
  55. if (path == nil) {
  56. desc = @"data size is 0";
  57. } else {
  58. desc = [[NSString alloc] initWithFormat:@"file %@ size is 0", path];
  59. }
  60. return [QNResponseInfo errorResponseInfo:kQNZeroDataSize
  61. errorDesc:desc];
  62. }
  63. + (instancetype)responseInfoWithLocalIOError:(NSString *)desc{
  64. return [QNResponseInfo errorResponseInfo:kQNLocalIOError
  65. errorDesc:desc];
  66. }
  67. + (instancetype)responseInfoWithMaliciousResponseError:(NSString *)desc{
  68. return [QNResponseInfo errorResponseInfo:kQNMaliciousResponseError
  69. errorDesc:desc];
  70. }
  71. + (instancetype)responseInfoWithNoUsableHostError:(NSString *)desc{
  72. return [QNResponseInfo errorResponseInfo:kQNSDKInteriorError
  73. errorDesc:desc];
  74. }
  75. + (instancetype)responseInfoWithSDKInteriorError:(NSString *)desc{
  76. return [QNResponseInfo errorResponseInfo:kQNSDKInteriorError
  77. errorDesc:desc];
  78. }
  79. + (instancetype)responseInfoWithUnexpectedSysCallError:(NSString *)desc{
  80. return [QNResponseInfo errorResponseInfo:kQNUnexpectedSysCallError
  81. errorDesc:desc];
  82. }
  83. + (instancetype)errorResponseInfo:(int)errorType
  84. errorDesc:(NSString *)errorDesc{
  85. return [self errorResponseInfo:errorType errorDesc:errorDesc error:nil];
  86. }
  87. + (instancetype)errorResponseInfo:(int)errorType
  88. errorDesc:(NSString *)errorDesc
  89. error:(NSError *)error{
  90. QNResponseInfo *response = [[QNResponseInfo alloc] init];
  91. response.statusCode = errorType;
  92. response.message = errorDesc;
  93. if (error) {
  94. response.error = error;
  95. } else {
  96. NSError *error = [[NSError alloc] initWithDomain:kQNErrorDomain
  97. code:errorType
  98. userInfo:@{ @"error" : response.message ?: @"error" }];
  99. response.error = error;
  100. }
  101. return response;
  102. }
  103. - (instancetype)initWithResponseInfoHost:(NSString *)host
  104. response:(NSHTTPURLResponse *)response
  105. body:(NSData *)body
  106. error:(NSError *)error {
  107. self = [super init];
  108. if (self) {
  109. _host = host;
  110. _timeStamp = [[NSDate date] timeIntervalSince1970];
  111. if (response) {
  112. int statusCode = (int)[response statusCode];
  113. NSDictionary *headers = [response allHeaderFields];
  114. _statusCode = statusCode;
  115. _reqId = headers[@"x-reqid"];
  116. _xlog = headers[@"x-log"];
  117. _xvia = headers[@"x-via"] ?: headers[@"x-px"] ?: headers[@"fw-via"];
  118. if (_statusCode == 200 && (_reqId == nil || _xlog == nil)) {
  119. _statusCode = kQNMaliciousResponseError;
  120. _message = @"this is a malicious response";
  121. _responseDictionary = nil;
  122. _error = [[NSError alloc] initWithDomain:kQNErrorDomain code:_statusCode userInfo:@{@"error" : _message}];
  123. } else if (error) {
  124. _error = error;
  125. _statusCode = (int)error.code;
  126. _message = [NSString stringWithFormat:@"%@", error];
  127. _responseDictionary = nil;
  128. } else {
  129. NSMutableDictionary *errorUserInfo = [@{@"errorHost" : host ?: @""} mutableCopy];
  130. if (!body) {
  131. _message = @"no response data";
  132. _error = nil;
  133. _responseDictionary = nil;
  134. } else {
  135. NSError *tmp = nil;
  136. NSDictionary *responseInfo = nil;
  137. responseInfo = [NSJSONSerialization JSONObjectWithData:body options:NSJSONReadingMutableLeaves error:&tmp];
  138. if (tmp){
  139. _message = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding];
  140. _error = nil;
  141. _responseDictionary = nil;
  142. } else if (statusCode >= 200 && statusCode < 300) {
  143. _error = nil;
  144. _message = @"ok";
  145. _responseDictionary = responseInfo;
  146. } else {
  147. NSString *errorString = responseInfo[@"error"];
  148. if (errorString) {
  149. [errorUserInfo setDictionary:@{@"error" : errorString}];
  150. _message = errorString;
  151. _error = [[NSError alloc] initWithDomain:kQNErrorDomain code:statusCode userInfo:errorUserInfo];
  152. } else {
  153. _message = errorString;
  154. _error = nil;
  155. }
  156. _responseDictionary = responseInfo;
  157. }
  158. }
  159. }
  160. } else if (error) {
  161. _error = error;
  162. _statusCode = (int)error.code;
  163. _message = [NSString stringWithFormat:@"%@", error];
  164. _responseDictionary = nil;
  165. } else {
  166. _statusCode = kQNSDKInteriorError;
  167. _message = @"no response";
  168. }
  169. }
  170. return self;
  171. }
  172. - (BOOL)isCancelled {
  173. return _statusCode == kQNRequestCancelled || _statusCode == -999;
  174. }
  175. - (BOOL)isTlsError{
  176. if (_statusCode == NSURLErrorServerCertificateHasBadDate
  177. || _statusCode == NSURLErrorClientCertificateRejected
  178. || _statusCode == NSURLErrorClientCertificateRequired) {
  179. return true;
  180. } else {
  181. return false;
  182. }
  183. }
  184. - (BOOL)isNotQiniu {
  185. // reqId is nill means the server is not qiniu
  186. return _reqId == nil || _xlog == nil;
  187. }
  188. - (BOOL)isOK {
  189. return (_statusCode >= 200 && _statusCode < 300) && _error == nil && _reqId != nil && _xlog != nil;
  190. }
  191. - (BOOL)couldRetry {
  192. if (self.isCancelled
  193. || _statusCode == 100
  194. || (_statusCode > 300 && _statusCode < 400)
  195. || (_statusCode > 400 && _statusCode < 500 && _statusCode != 406)
  196. || _statusCode == 501 || _statusCode == 573
  197. || _statusCode == 608 || _statusCode == 612 || _statusCode == 614 || _statusCode == 616
  198. || _statusCode == 619 || _statusCode == 630 || _statusCode == 631 || _statusCode == 640
  199. || _statusCode == 701
  200. || (_statusCode < -1 && _statusCode > -1000)) {
  201. return NO;
  202. } else {
  203. return YES;
  204. }
  205. }
  206. - (BOOL)couldRegionRetry{
  207. if ([self couldRetry] == NO || _statusCode == 400 || _statusCode == 579 ) {
  208. return NO;
  209. } else {
  210. return YES;
  211. }
  212. }
  213. - (BOOL)couldHostRetry{
  214. if ([self couldRegionRetry] == NO
  215. || _statusCode == 502 || _statusCode == 503 || _statusCode == 571 || _statusCode == 599) {
  216. return NO;
  217. } else {
  218. return YES;
  219. }
  220. }
  221. - (BOOL)canConnectToHost{
  222. if (_statusCode > 99 || self.isCancelled) {
  223. return true;
  224. } else {
  225. return false;
  226. }
  227. }
  228. - (BOOL)isHostUnavailable{
  229. // 基本不可恢复,注:会影响下次请求,范围太大可能会造成大量的timeout
  230. if (_statusCode == 502 || _statusCode == 503 || _statusCode == 504 || _statusCode == 599) {
  231. return true;
  232. } else {
  233. return false;
  234. }
  235. }
  236. - (BOOL)isConnectionBroken {
  237. return _statusCode == kQNNetworkError || _statusCode == NSURLErrorNotConnectedToInternet;
  238. }
  239. - (NSString *)description {
  240. return [NSString stringWithFormat:@"<%@= id: %@, ver: %@, status: %d, requestId: %@, xlog: %@, xvia: %@, host: %@ time: %llu error: %@>", NSStringFromClass([self class]), _id, [QNUtils sdkVersion], _statusCode, _reqId, _xlog, _xvia, _host, _timeStamp, _error];
  241. }
  242. @end