QNSessionManager.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. //
  2. // QNHttpManager.m
  3. // QiniuSDK
  4. //
  5. // Created by bailong on 14/10/1.
  6. // Copyright (c) 2014年 Qiniu. All rights reserved.
  7. //
  8. #import "QNAsyncRun.h"
  9. #import "QNConfiguration.h"
  10. #import "QNResponseInfo.h"
  11. #import "QNSessionManager.h"
  12. #include "QNSystem.h"
  13. #import "QNUserAgent.h"
  14. #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090)
  15. @interface QNProgessDelegate : NSObject <NSURLSessionDataDelegate>
  16. @property (nonatomic, strong) QNInternalProgressBlock progressBlock;
  17. @property (nonatomic, strong) NSURLSessionUploadTask *task;
  18. @property (nonatomic, strong) QNCancelBlock cancelBlock;
  19. - (instancetype)initWithProgress:(QNInternalProgressBlock)progressBlock;
  20. @end
  21. static NSURL *buildUrl(NSString *host, NSNumber *port, NSString *path) {
  22. port = port == nil ? [NSNumber numberWithInt:80] : port;
  23. NSString *p = [[NSString alloc] initWithFormat:@"http://%@:%@%@", host, port, path];
  24. return [[NSURL alloc] initWithString:p];
  25. }
  26. static BOOL needRetry(NSHTTPURLResponse *httpResponse, NSError *error) {
  27. if (error != nil) {
  28. return error.code < -1000;
  29. }
  30. if (httpResponse == nil) {
  31. return YES;
  32. }
  33. int status = (int)httpResponse.statusCode;
  34. return status >= 500 && status < 600 && status != 579;
  35. }
  36. @implementation QNProgessDelegate
  37. - (instancetype)initWithProgress:(QNInternalProgressBlock)progressBlock {
  38. if (self = [super init]) {
  39. _progressBlock = progressBlock;
  40. }
  41. return self;
  42. }
  43. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
  44. didSendBodyData:(int64_t)bytesSent
  45. totalBytesSent:(int64_t)totalBytesSent
  46. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
  47. if (_progressBlock) {
  48. _progressBlock(totalBytesSent, totalBytesExpectedToSend);
  49. }
  50. if (_cancelBlock && _cancelBlock()) {
  51. [_task cancel];
  52. }
  53. }
  54. @end
  55. @interface QNSessionManager ()
  56. @property UInt32 timeout;
  57. @property (nonatomic, strong) QNUrlConvert converter;
  58. @property bool noProxy;
  59. @property (nonatomic, strong) NSDictionary *proxyDict;
  60. @property (nonatomic, strong) NSOperationQueue *delegateQueue;
  61. @end
  62. @implementation QNSessionManager
  63. - (instancetype)initWithProxy:(NSDictionary *)proxyDict
  64. timeout:(UInt32)timeout
  65. urlConverter:(QNUrlConvert)converter {
  66. if (self = [super init]) {
  67. if (proxyDict != nil) {
  68. _noProxy = NO;
  69. _proxyDict = proxyDict;
  70. } else {
  71. _noProxy = YES;
  72. }
  73. _delegateQueue = [[NSOperationQueue alloc] init];
  74. _timeout = timeout;
  75. _converter = converter;
  76. }
  77. return self;
  78. }
  79. - (instancetype)init {
  80. return [self initWithProxy:nil timeout:60 urlConverter:nil];
  81. }
  82. + (QNResponseInfo *)buildResponseInfo:(NSHTTPURLResponse *)response
  83. withError:(NSError *)error
  84. withDuration:(double)duration
  85. withResponse:(NSData *)body
  86. withHost:(NSString *)host
  87. withIp:(NSString *)ip {
  88. QNResponseInfo *info;
  89. if (response) {
  90. int status = (int)[response statusCode];
  91. NSDictionary *headers = [response allHeaderFields];
  92. NSString *reqId = headers[@"X-Reqid"];
  93. NSString *xlog = headers[@"X-Log"];
  94. NSString *xvia = headers[@"X-Via"];
  95. if (xvia == nil) {
  96. xvia = headers[@"X-Px"];
  97. }
  98. if (xvia == nil) {
  99. xvia = headers[@"Fw-Via"];
  100. }
  101. info = [[QNResponseInfo alloc] init:status withReqId:reqId withXLog:xlog withXVia:xvia withHost:host withIp:ip withDuration:duration withBody:body];
  102. } else {
  103. info = [QNResponseInfo responseInfoWithNetError:error host:host duration:duration];
  104. }
  105. return info;
  106. }
  107. - (void)sendRequest:(NSMutableURLRequest *)request
  108. withCompleteBlock:(QNCompleteBlock)completeBlock
  109. withProgressBlock:(QNInternalProgressBlock)progressBlock
  110. withCancelBlock:(QNCancelBlock)cancelBlock
  111. withAccess:(NSString *)access {
  112. __block NSDate *startTime = [NSDate date];
  113. NSString *domain = request.URL.host;
  114. NSString *u = request.URL.absoluteString;
  115. NSURL *url = request.URL;
  116. NSArray *ips = nil;
  117. if (_converter != nil) {
  118. url = [[NSURL alloc] initWithString:_converter(u)];
  119. request.URL = url;
  120. domain = url.host;
  121. }
  122. [self sendRequest2:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock withIpArray:ips withIndex:0 withDomain:domain withRetryTimes:3 withStartTime:startTime withAccess:access];
  123. }
  124. - (void)sendRequest2:(NSMutableURLRequest *)request
  125. withCompleteBlock:(QNCompleteBlock)completeBlock
  126. withProgressBlock:(QNInternalProgressBlock)progressBlock
  127. withCancelBlock:(QNCancelBlock)cancelBlock
  128. withIpArray:(NSArray *)ips
  129. withIndex:(int)index
  130. withDomain:(NSString *)domain
  131. withRetryTimes:(int)times
  132. withStartTime:(NSDate *)startTime
  133. withAccess:(NSString *)access {
  134. NSURL *url = request.URL;
  135. __block NSString *ip = nil;
  136. if (ips != nil) {
  137. ip = [ips objectAtIndex:(index % ips.count)];
  138. NSString *path = url.path;
  139. if (path == nil || [@"" isEqualToString:path]) {
  140. path = @"/";
  141. }
  142. url = buildUrl(ip, url.port, path);
  143. [request setValue:domain forHTTPHeaderField:@"Host"];
  144. }
  145. request.URL = url;
  146. [request setTimeoutInterval:_timeout];
  147. [request setValue:[[QNUserAgent sharedInstance] getUserAgent:access] forHTTPHeaderField:@"User-Agent"];
  148. [request setValue:nil forHTTPHeaderField:@"Accept-Language"];
  149. if (progressBlock == nil) {
  150. progressBlock = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
  151. };
  152. }
  153. QNInternalProgressBlock progressBlock2 = ^(long long totalBytesWritten, long long totalBytesExpectedToWrite) {
  154. progressBlock(totalBytesWritten, totalBytesExpectedToWrite);
  155. };
  156. __block QNProgessDelegate *delegate = [[QNProgessDelegate alloc] initWithProgress:nil];
  157. delegate.progressBlock = progressBlock2;
  158. NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  159. if (_proxyDict) {
  160. configuration.connectionProxyDictionary = _proxyDict;
  161. }
  162. __block NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:_delegateQueue];
  163. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:nil completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  164. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  165. double duration = [[NSDate date] timeIntervalSinceDate:startTime];
  166. QNResponseInfo *info;
  167. NSDictionary *resp = nil;
  168. if (_converter != nil && _noProxy && (index + 1 < ips.count || times > 0) && needRetry(httpResponse, error)) {
  169. [self sendRequest2:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock withIpArray:ips withIndex:index + 1 withDomain:domain withRetryTimes:times - 1 withStartTime:startTime withAccess:access];
  170. return;
  171. }
  172. if (error == nil) {
  173. info = [QNSessionManager buildResponseInfo:httpResponse withError:nil withDuration:duration withResponse:data withHost:domain withIp:ip];
  174. if (info.isOK) {
  175. NSError *tmp;
  176. resp = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&tmp];
  177. }
  178. } else {
  179. info = [QNSessionManager buildResponseInfo:httpResponse withError:error withDuration:duration withResponse:data withHost:domain withIp:ip];
  180. }
  181. delegate.task = nil;
  182. delegate.cancelBlock = nil;
  183. delegate.progressBlock = nil;
  184. completeBlock(info, resp);
  185. [session finishTasksAndInvalidate];
  186. }];
  187. delegate.task = uploadTask;
  188. delegate.cancelBlock = cancelBlock;
  189. [uploadTask resume];
  190. }
  191. - (void)multipartPost:(NSString *)url
  192. withData:(NSData *)data
  193. withParams:(NSDictionary *)params
  194. withFileName:(NSString *)key
  195. withMimeType:(NSString *)mime
  196. withCompleteBlock:(QNCompleteBlock)completeBlock
  197. withProgressBlock:(QNInternalProgressBlock)progressBlock
  198. withCancelBlock:(QNCancelBlock)cancelBlock
  199. withAccess:(NSString *)access {
  200. NSURL *URL = [[NSURL alloc] initWithString:url];
  201. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
  202. request.HTTPMethod = @"POST";
  203. NSString *boundary = @"werghnvt54wef654rjuhgb56trtg34tweuyrgf";
  204. request.allHTTPHeaderFields = @{
  205. @"Content-Type" : [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]
  206. };
  207. NSMutableData *postData = [[NSMutableData alloc] init];
  208. for (NSString *paramsKey in params) {
  209. NSString *pair = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n", boundary, paramsKey];
  210. [postData appendData:[pair dataUsingEncoding:NSUTF8StringEncoding]];
  211. id value = [params objectForKey:paramsKey];
  212. if ([value isKindOfClass:[NSString class]]) {
  213. [postData appendData:[value dataUsingEncoding:NSUTF8StringEncoding]];
  214. } else if ([value isKindOfClass:[NSData class]]) {
  215. [postData appendData:value];
  216. }
  217. [postData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
  218. }
  219. NSString *filePair = [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\nContent-Type:%@\r\n\r\n", boundary, @"file", key, mime];
  220. [postData appendData:[filePair dataUsingEncoding:NSUTF8StringEncoding]];
  221. [postData appendData:data];
  222. [postData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
  223. request.HTTPBody = postData;
  224. [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)postData.length] forHTTPHeaderField:@"Content-Length"];
  225. [self sendRequest:request withCompleteBlock:completeBlock withProgressBlock:progressBlock withCancelBlock:cancelBlock
  226. withAccess:access];
  227. }
  228. - (void)post:(NSString *)url
  229. withData:(NSData *)data
  230. withParams:(NSDictionary *)params
  231. withHeaders:(NSDictionary *)headers
  232. withCompleteBlock:(QNCompleteBlock)completeBlock
  233. withProgressBlock:(QNInternalProgressBlock)progressBlock
  234. withCancelBlock:(QNCancelBlock)cancelBlock
  235. withAccess:(NSString *)access {
  236. NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[[NSURL alloc] initWithString:url]];
  237. if (headers) {
  238. [request setAllHTTPHeaderFields:headers];
  239. }
  240. [request setHTTPMethod:@"POST"];
  241. if (params) {
  242. [request setValuesForKeysWithDictionary:params];
  243. }
  244. [request setHTTPBody:data];
  245. QNAsyncRun(^{
  246. [self sendRequest:request
  247. withCompleteBlock:completeBlock
  248. withProgressBlock:progressBlock
  249. withCancelBlock:cancelBlock
  250. withAccess:access];
  251. });
  252. }
  253. - (void)get:(NSString *)url
  254. withHeaders:(NSDictionary *)headers
  255. withCompleteBlock:(QNCompleteBlock)completeBlock {
  256. QNAsyncRun(^{
  257. NSURL *URL = [NSURL URLWithString:url];
  258. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  259. NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  260. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  261. NSData *s = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
  262. NSDictionary *resp = nil;
  263. QNResponseInfo *info;
  264. if (error == nil) {
  265. info = [QNSessionManager buildResponseInfo:httpResponse withError:nil withDuration:0 withResponse:s withHost:@"" withIp:@""];
  266. if (info.isOK) {
  267. NSError *jsonError;
  268. id unMarshel = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
  269. if (jsonError) {
  270. info = [QNSessionManager buildResponseInfo:httpResponse withError:jsonError withDuration:0 withResponse:s withHost:@"" withIp:@""];
  271. } else if ([unMarshel isKindOfClass:[NSDictionary class]]) {
  272. resp = unMarshel;
  273. }
  274. }
  275. } else {
  276. info = [QNSessionManager buildResponseInfo:httpResponse withError:error withDuration:0 withResponse:s withHost:@"" withIp:@""];
  277. }
  278. completeBlock(info, resp);
  279. }];
  280. [dataTask resume];
  281. });
  282. }
  283. @end
  284. #endif