No Description

RNFetchBlobNetwork.m 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. //
  2. // RNFetchBlobNetwork.m
  3. // RNFetchBlob
  4. //
  5. // Created by wkh237 on 2016/6/6.
  6. // Copyright © 2016 wkh237. All rights reserved.
  7. //
  8. #import <Foundation/Foundation.h>
  9. #import "RNFetchBlob.h"
  10. #import "RNFetchBlobFS.h"
  11. #import "RNFetchBlobNetwork.h"
  12. #import "RNFetchBlobConst.h"
  13. #import "RNFetchBlobReqBuilder.h"
  14. #import "IOS7Polyfill.h"
  15. #import <CommonCrypto/CommonDigest.h>
  16. #import "RNFetchBlobProgress.h"
  17. #if __has_include(<React/RCTAssert.h>)
  18. #import <React/RCTRootView.h>
  19. #import <React/RCTLog.h>
  20. #import <React/RCTEventDispatcher.h>
  21. #import <React/RCTBridge.h>
  22. #else
  23. #import "RCTRootView.h"
  24. #import "RCTLog.h"
  25. #import "RCTEventDispatcher.h"
  26. #import "RCTBridge.h"
  27. #endif
  28. ////////////////////////////////////////
  29. //
  30. // HTTP request handler
  31. //
  32. ////////////////////////////////////////
  33. NSMapTable * taskTable;
  34. NSMapTable * expirationTable;
  35. NSMutableDictionary * progressTable;
  36. NSMutableDictionary * uploadProgressTable;
  37. __attribute__((constructor))
  38. static void initialize_tables() {
  39. if(expirationTable == nil)
  40. {
  41. expirationTable = [[NSMapTable alloc] init];
  42. }
  43. if(taskTable == nil)
  44. {
  45. taskTable = [[NSMapTable alloc] init];
  46. }
  47. if(progressTable == nil)
  48. {
  49. progressTable = [[NSMutableDictionary alloc] init];
  50. }
  51. if(uploadProgressTable == nil)
  52. {
  53. uploadProgressTable = [[NSMutableDictionary alloc] init];
  54. }
  55. }
  56. @implementation RNFetchBlobNetwork
  57. NSOperationQueue *taskQueue;
  58. @synthesize taskId;
  59. @synthesize expectedBytes;
  60. @synthesize receivedBytes;
  61. @synthesize respData;
  62. @synthesize callback;
  63. @synthesize bridge;
  64. @synthesize options;
  65. @synthesize redirects;
  66. @synthesize fileTaskCompletionHandler;
  67. @synthesize dataTaskCompletionHandler;
  68. @synthesize error;
  69. @synthesize respFile;
  70. @synthesize isNewPart;
  71. @synthesize isIncrement;
  72. @synthesize partBuffer;
  73. @synthesize destPath;
  74. @synthesize writeStream;
  75. @synthesize bodyLength;
  76. @synthesize respInfo;
  77. @synthesize respStatus;
  78. @synthesize responseFormat;
  79. @synthesize followRedirect;
  80. // constructor
  81. - (id)init {
  82. self = [super init];
  83. if(taskQueue == nil) {
  84. taskQueue = [[NSOperationQueue alloc] init];
  85. taskQueue.maxConcurrentOperationCount = 10;
  86. }
  87. return self;
  88. }
  89. + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config
  90. {
  91. if(progressTable == nil)
  92. {
  93. progressTable = [[NSMutableDictionary alloc] init];
  94. }
  95. [progressTable setValue:config forKey:taskId];
  96. }
  97. + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config
  98. {
  99. if(uploadProgressTable == nil)
  100. {
  101. uploadProgressTable = [[NSMutableDictionary alloc] init];
  102. }
  103. [uploadProgressTable setValue:config forKey:taskId];
  104. }
  105. // removing case from headers
  106. + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers
  107. {
  108. NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init];
  109. for(NSString * key in headers) {
  110. [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]];
  111. }
  112. return mheaders;
  113. }
  114. - (NSString *)md5:(NSString *)input {
  115. const char* str = [input UTF8String];
  116. unsigned char result[CC_MD5_DIGEST_LENGTH];
  117. CC_MD5(str, (CC_LONG)strlen(str), result);
  118. NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
  119. for(int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
  120. [ret appendFormat:@"%02x",result[i]];
  121. }
  122. return ret;
  123. }
  124. // send HTTP request
  125. - (void) sendRequest:(__weak NSDictionary * _Nullable )options
  126. contentLength:(long) contentLength
  127. bridge:(RCTBridge * _Nullable)bridgeRef
  128. taskId:(NSString * _Nullable)taskId
  129. withRequest:(__weak NSURLRequest * _Nullable)req
  130. callback:(_Nullable RCTResponseSenderBlock) callback
  131. {
  132. self.taskId = taskId;
  133. self.respData = [[NSMutableData alloc] initWithLength:0];
  134. self.callback = callback;
  135. self.bridge = bridgeRef;
  136. self.expectedBytes = 0;
  137. self.receivedBytes = 0;
  138. self.options = options;
  139. followRedirect = [options valueForKey:@"followRedirect"] == nil ? YES : [[options valueForKey:@"followRedirect"] boolValue];
  140. isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
  141. redirects = [[NSMutableArray alloc] init];
  142. if(req.URL != nil)
  143. [redirects addObject:req.URL.absoluteString];
  144. // set response format
  145. NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
  146. if([[rnfbResp lowercaseString] isEqualToString:@"base64"])
  147. responseFormat = BASE64;
  148. else if([[rnfbResp lowercaseString] isEqualToString:@"utf8"])
  149. responseFormat = UTF8;
  150. else
  151. responseFormat = AUTO;
  152. NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
  153. NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
  154. NSString * key = [self.options valueForKey:CONFIG_KEY];
  155. __block NSURLSession * session;
  156. bodyLength = contentLength;
  157. // the session trust any SSL certification
  158. NSURLSessionConfiguration *defaultConfigObject;
  159. if(!followRedirect)
  160. {
  161. defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
  162. }
  163. else
  164. {
  165. NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
  166. }
  167. // set request timeout
  168. float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
  169. if(timeout > 0)
  170. {
  171. defaultConfigObject.timeoutIntervalForRequest = timeout/1000;
  172. }
  173. defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
  174. session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue];
  175. if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil)
  176. {
  177. respFile = YES;
  178. NSString* cacheKey = taskId;
  179. if (key != nil) {
  180. cacheKey = [self md5:key];
  181. if (cacheKey == nil) {
  182. cacheKey = taskId;
  183. }
  184. destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
  185. if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
  186. callback(@[[NSNull null], RESP_TYPE_PATH, destPath]);
  187. return;
  188. }
  189. }
  190. if(path != nil)
  191. destPath = path;
  192. else
  193. destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
  194. }
  195. else
  196. {
  197. respData = [[NSMutableData alloc] init];
  198. respFile = NO;
  199. }
  200. __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
  201. [taskTable setObject:task forKey:taskId];
  202. [task resume];
  203. // network status indicator
  204. if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
  205. [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
  206. __block UIApplication * app = [UIApplication sharedApplication];
  207. // #115 handling task expired when application entering backgound for a long time
  208. UIBackgroundTaskIdentifier tid = [app beginBackgroundTaskWithName:taskId expirationHandler:^{
  209. NSLog([NSString stringWithFormat:@"session %@ expired", taskId ]);
  210. [expirationTable setObject:task forKey:taskId];
  211. [app endBackgroundTask:tid];
  212. }];
  213. }
  214. // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
  215. + (void) emitExpiredTasks
  216. {
  217. NSEnumerator * emu = [expirationTable keyEnumerator];
  218. NSString * key;
  219. while((key = [emu nextObject]))
  220. {
  221. RCTBridge * bridge = [RNFetchBlob getRCTBridge];
  222. NSData * args = @{ @"taskId": key };
  223. [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args];
  224. }
  225. // clear expired task entries
  226. [expirationTable removeAllObjects];
  227. expirationTable = [[NSMapTable alloc] init];
  228. }
  229. ////////////////////////////////////////
  230. //
  231. // NSURLSession delegates
  232. //
  233. ////////////////////////////////////////
  234. #pragma mark NSURLSession delegate methods
  235. #pragma mark - Received Response
  236. // set expected content length on response received
  237. - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
  238. {
  239. expectedBytes = [response expectedContentLength];
  240. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
  241. NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
  242. NSString * respType = @"";
  243. respStatus = statusCode;
  244. if ([response respondsToSelector:@selector(allHeaderFields)])
  245. {
  246. NSDictionary *headers = [httpResponse allHeaderFields];
  247. NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
  248. if(self.isServerPush == NO)
  249. {
  250. self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"];
  251. }
  252. if(self.isServerPush)
  253. {
  254. if(partBuffer != nil)
  255. {
  256. [self.bridge.eventDispatcher
  257. sendDeviceEventWithName:EVENT_SERVER_PUSH
  258. body:@{
  259. @"taskId": taskId,
  260. @"chunk": [partBuffer base64EncodedStringWithOptions:0],
  261. }
  262. ];
  263. }
  264. partBuffer = [[NSMutableData alloc] init];
  265. completionHandler(NSURLSessionResponseAllow);
  266. return;
  267. }
  268. if(respCType != nil)
  269. {
  270. NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
  271. if([respCType RNFBContainsString:@"text/"])
  272. {
  273. respType = @"text";
  274. }
  275. else if([respCType RNFBContainsString:@"application/json"])
  276. {
  277. respType = @"json";
  278. }
  279. // If extra blob content type is not empty, check if response type matches
  280. else if( extraBlobCTypes != nil) {
  281. for(NSString * substr in extraBlobCTypes)
  282. {
  283. if([respCType RNFBContainsString:[substr lowercaseString]])
  284. {
  285. respType = @"blob";
  286. respFile = YES;
  287. destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil];
  288. break;
  289. }
  290. }
  291. }
  292. else
  293. {
  294. respType = @"blob";
  295. // for XMLHttpRequest, switch response data handling strategy automatically
  296. if([options valueForKey:@"auto"] == YES) {
  297. respFile = YES;
  298. destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""];
  299. }
  300. }
  301. }
  302. else
  303. respType = @"text";
  304. respInfo = @{
  305. @"taskId": taskId,
  306. @"state": @"2",
  307. @"headers": headers,
  308. @"redirects": redirects,
  309. @"respType" : respType,
  310. @"timeout" : @NO,
  311. @"status": [NSNumber numberWithInteger:statusCode]
  312. };
  313. #pragma mark - handling cookies
  314. // # 153 get cookies
  315. if(response.URL != nil)
  316. {
  317. NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  318. NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
  319. if(cookies != nil && [cookies count] > 0) {
  320. [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil];
  321. }
  322. }
  323. [self.bridge.eventDispatcher
  324. sendDeviceEventWithName: EVENT_STATE_CHANGE
  325. body:respInfo
  326. ];
  327. headers = nil;
  328. respInfo = nil;
  329. }
  330. else
  331. NSLog(@"oops");
  332. if(respFile == YES)
  333. {
  334. @try{
  335. NSFileManager * fm = [NSFileManager defaultManager];
  336. NSString * folder = [destPath stringByDeletingLastPathComponent];
  337. if(![fm fileExistsAtPath:folder])
  338. {
  339. [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
  340. }
  341. BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
  342. BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"];
  343. appendToExistingFile = !overwrite;
  344. // For solving #141 append response data if the file already exists
  345. // base on PR#139 @kejinliang
  346. if(appendToExistingFile)
  347. {
  348. destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
  349. }
  350. if (![fm fileExistsAtPath:destPath])
  351. {
  352. [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
  353. }
  354. writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
  355. [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
  356. [writeStream open];
  357. }
  358. @catch(NSException * ex)
  359. {
  360. NSLog(@"write file error");
  361. }
  362. }
  363. completionHandler(NSURLSessionResponseAllow);
  364. }
  365. // download progress handler
  366. - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
  367. {
  368. // For #143 handling multipart/x-mixed-replace response
  369. if(self.isServerPush)
  370. {
  371. [partBuffer appendData:data];
  372. return ;
  373. }
  374. NSNumber * received = [NSNumber numberWithLong:[data length]];
  375. receivedBytes += [received longValue];
  376. NSString * chunkString = @"";
  377. if(isIncrement == YES)
  378. {
  379. chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  380. }
  381. if(respFile == NO)
  382. {
  383. [respData appendData:data];
  384. }
  385. else
  386. {
  387. [writeStream write:[data bytes] maxLength:[data length]];
  388. }
  389. RNFetchBlobProgress * pconfig = [progressTable valueForKey:taskId];
  390. if(expectedBytes == 0)
  391. return;
  392. NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
  393. if(pconfig != nil && [pconfig shouldReport:now])
  394. {
  395. [self.bridge.eventDispatcher
  396. sendDeviceEventWithName:EVENT_PROGRESS
  397. body:@{
  398. @"taskId": taskId,
  399. @"written": [NSString stringWithFormat:@"%d", receivedBytes],
  400. @"total": [NSString stringWithFormat:@"%d", expectedBytes],
  401. @"chunk": chunkString
  402. }
  403. ];
  404. }
  405. received = nil;
  406. }
  407. - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
  408. {
  409. if([session isEqual:session])
  410. session = nil;
  411. }
  412. - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
  413. {
  414. self.error = error;
  415. NSString * errMsg = [NSNull null];
  416. NSString * respStr = [NSNull null];
  417. NSString * rnfbRespType = @"";
  418. [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
  419. if(respInfo == nil)
  420. {
  421. respInfo = [NSNull null];
  422. }
  423. if(error != nil)
  424. {
  425. errMsg = [error localizedDescription];
  426. }
  427. if(respFile == YES)
  428. {
  429. [writeStream close];
  430. rnfbRespType = RESP_TYPE_PATH;
  431. respStr = destPath;
  432. }
  433. // base64 response
  434. else {
  435. // #73 fix unicode data encoding issue :
  436. // when response type is BASE64, we should first try to encode the response data to UTF8 format
  437. // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
  438. // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
  439. NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
  440. if(responseFormat == BASE64)
  441. {
  442. rnfbRespType = RESP_TYPE_BASE64;
  443. respStr = [respData base64EncodedStringWithOptions:0];
  444. }
  445. else if (responseFormat == UTF8)
  446. {
  447. rnfbRespType = RESP_TYPE_UTF8;
  448. respStr = utf8;
  449. }
  450. else
  451. {
  452. if(utf8 != nil)
  453. {
  454. rnfbRespType = RESP_TYPE_UTF8;
  455. respStr = utf8;
  456. }
  457. else
  458. {
  459. rnfbRespType = RESP_TYPE_BASE64;
  460. respStr = [respData base64EncodedStringWithOptions:0];
  461. }
  462. }
  463. }
  464. callback(@[ errMsg, rnfbRespType, respStr]);
  465. @synchronized(taskTable, uploadProgressTable, progressTable)
  466. {
  467. if([taskTable objectForKey:taskId] == nil)
  468. NSLog(@"object released by ARC.");
  469. else
  470. [taskTable removeObjectForKey:taskId];
  471. [uploadProgressTable removeObjectForKey:taskId];
  472. [progressTable removeObjectForKey:taskId];
  473. }
  474. respData = nil;
  475. receivedBytes = 0;
  476. [session finishTasksAndInvalidate];
  477. }
  478. // upload progress handler
  479. - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
  480. {
  481. RNFetchBlobProgress * pconfig = [uploadProgressTable valueForKey:taskId];
  482. if(totalBytesExpectedToWrite == 0)
  483. return;
  484. NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
  485. if(pconfig != nil && [pconfig shouldReport:now]) {
  486. [self.bridge.eventDispatcher
  487. sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD
  488. body:@{
  489. @"taskId": taskId,
  490. @"written": [NSString stringWithFormat:@"%d", totalBytesWritten],
  491. @"total": [NSString stringWithFormat:@"%d", totalBytesExpectedToWrite]
  492. }
  493. ];
  494. }
  495. }
  496. # pragma mark - cookies handling API
  497. + (NSDictionary *) getCookies:(NSString *) domain
  498. {
  499. NSMutableDictionary * result = [NSMutableDictionary new];
  500. NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  501. for(NSHTTPCookie * cookie in [cookieStore cookies])
  502. {
  503. NSString * cDomain = [cookie domain];
  504. if([result objectForKey:cDomain] == nil)
  505. {
  506. [result setObject:[NSMutableArray new] forKey:cDomain];
  507. }
  508. if([cDomain isEqualToString:domain] || [domain length] == 0)
  509. {
  510. NSMutableString * cookieStr = [[NSMutableString alloc] init];
  511. cookieStr = [[self class] getCookieString:cookie];
  512. NSMutableArray * ary = [result objectForKey:cDomain];
  513. [ary addObject:cookieStr];
  514. [result setObject:ary forKey:cDomain];
  515. }
  516. }
  517. return result;
  518. }
  519. // remove cookies for given domain, if domain is empty remove all cookies in shared cookie storage.
  520. + (void) removeCookies:(NSString *) domain error:(NSError **)error
  521. {
  522. @try
  523. {
  524. NSHTTPCookieStorage * cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  525. for(NSHTTPCookie * cookie in [cookies cookies])
  526. {
  527. BOOL shouldRemove = domain == nil || [domain length] < 1 || [[cookie domain] isEqualToString:domain];
  528. if(shouldRemove)
  529. {
  530. [cookies deleteCookie:cookie];
  531. }
  532. }
  533. }
  534. @catch(NSError * err)
  535. {
  536. *error = err;
  537. }
  538. }
  539. // convert NSHTTPCookie to string
  540. + (NSString *) getCookieString:(NSHTTPCookie *) cookie
  541. {
  542. NSMutableString * cookieStr = [[NSMutableString alloc] init];
  543. [cookieStr appendString:cookie.name];
  544. [cookieStr appendString:@"="];
  545. [cookieStr appendString:cookie.value];
  546. if(cookie.expiresDate == nil) {
  547. [cookieStr appendString:@"; max-age=0"];
  548. }
  549. else {
  550. [cookieStr appendString:@"; expires="];
  551. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  552. [dateFormatter setDateFormat:@"EEE, dd MM yyyy HH:mm:ss ZZZ"];
  553. NSString *strDate = [dateFormatter stringFromDate:cookie.expiresDate];
  554. [cookieStr appendString:strDate];
  555. }
  556. [cookieStr appendString:@"; domain="];
  557. [cookieStr appendString: [cookie domain]];
  558. [cookieStr appendString:@"; path="];
  559. [cookieStr appendString:cookie.path];
  560. if (cookie.isSecure) {
  561. [cookieStr appendString:@"; secure"];
  562. }
  563. if (cookie.isHTTPOnly) {
  564. [cookieStr appendString:@"; httponly"];
  565. }
  566. return cookieStr;
  567. }
  568. + (void) cancelRequest:(NSString *)taskId
  569. {
  570. NSURLSessionDataTask * task = [taskTable objectForKey:taskId];
  571. if(task != nil && task.state == NSURLSessionTaskStateRunning)
  572. [task cancel];
  573. }
  574. - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
  575. {
  576. BOOL trusty = [options valueForKey:CONFIG_TRUSTY];
  577. if(!trusty)
  578. {
  579. completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
  580. }
  581. else
  582. {
  583. completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
  584. }
  585. }
  586. - (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
  587. {
  588. NSLog(@"sess done in background");
  589. }
  590. - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
  591. {
  592. if(followRedirect)
  593. {
  594. if(request.URL != nil)
  595. [redirects addObject:[request.URL absoluteString]];
  596. completionHandler(request);
  597. }
  598. else
  599. {
  600. completionHandler(nil);
  601. }
  602. }
  603. @end