RCTMultipartDataTask.m 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTMultipartDataTask.h"
  8. @interface RCTMultipartDataTask () <NSURLSessionDataDelegate, NSURLSessionDataDelegate>
  9. @end
  10. @implementation RCTMultipartDataTask {
  11. NSURL *_url;
  12. RCTMultipartDataTaskCallback _partHandler;
  13. RCTMultipartProgressCallback _progressHandler;
  14. NSInteger _statusCode;
  15. NSDictionary *_headers;
  16. NSString *_boundary;
  17. NSMutableData *_data;
  18. }
  19. - (instancetype)initWithURL:(NSURL *)url
  20. partHandler:(RCTMultipartDataTaskCallback)partHandler
  21. progressHandler:(RCTMultipartProgressCallback)progressHandler
  22. {
  23. if (self = [super init]) {
  24. _url = url;
  25. _partHandler = [partHandler copy];
  26. _progressHandler = [progressHandler copy];
  27. }
  28. return self;
  29. }
  30. - (void)startTask
  31. {
  32. NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
  33. delegate:self
  34. delegateQueue:nil];
  35. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url];
  36. [request addValue:@"multipart/mixed" forHTTPHeaderField:@"Accept"];
  37. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
  38. [dataTask resume];
  39. [session finishTasksAndInvalidate];
  40. }
  41. - (void)URLSession:(__unused NSURLSession *)session
  42. dataTask:(__unused NSURLSessionDataTask *)dataTask
  43. didReceiveResponse:(NSURLResponse *)response
  44. completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
  45. {
  46. if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
  47. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
  48. _headers = [httpResponse allHeaderFields];
  49. _statusCode = [httpResponse statusCode];
  50. NSString *contentType = @"";
  51. for (NSString *key in [_headers keyEnumerator]) {
  52. if ([[key lowercaseString] isEqualToString:@"content-type"]) {
  53. contentType = [_headers valueForKey:key];
  54. break;
  55. }
  56. }
  57. NSRegularExpression *regex =
  58. [NSRegularExpression regularExpressionWithPattern:@"multipart/mixed;.*boundary=\"([^\"]+)\""
  59. options:0
  60. error:nil];
  61. NSTextCheckingResult *match = [regex firstMatchInString:contentType
  62. options:0
  63. range:NSMakeRange(0, contentType.length)];
  64. if (match) {
  65. _boundary = [contentType substringWithRange:[match rangeAtIndex:1]];
  66. completionHandler(NSURLSessionResponseBecomeStream);
  67. return;
  68. }
  69. }
  70. // In case the server doesn't support multipart/mixed responses, fallback to normal download
  71. _data = [[NSMutableData alloc] initWithCapacity:1024 * 1024];
  72. completionHandler(NSURLSessionResponseAllow);
  73. }
  74. - (void)URLSession:(__unused NSURLSession *)session
  75. task:(__unused NSURLSessionTask *)task
  76. didCompleteWithError:(NSError *)error
  77. {
  78. if (_partHandler) {
  79. _partHandler(_statusCode, _headers, _data, error, YES);
  80. }
  81. }
  82. - (void)URLSession:(__unused NSURLSession *)session
  83. dataTask:(__unused NSURLSessionDataTask *)dataTask
  84. didReceiveData:(NSData *)data
  85. {
  86. [_data appendData:data];
  87. }
  88. - (void)URLSession:(__unused NSURLSession *)session
  89. dataTask:(__unused NSURLSessionDataTask *)dataTask
  90. didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask
  91. {
  92. [streamTask captureStreams];
  93. }
  94. - (void)URLSession:(__unused NSURLSession *)session
  95. streamTask:(__unused NSURLSessionStreamTask *)streamTask
  96. didBecomeInputStream:(NSInputStream *)inputStream
  97. outputStream:(__unused NSOutputStream *)outputStream
  98. {
  99. RCTMultipartStreamReader *reader = [[RCTMultipartStreamReader alloc] initWithInputStream:inputStream
  100. boundary:_boundary];
  101. RCTMultipartDataTaskCallback partHandler = _partHandler;
  102. _partHandler = nil;
  103. NSInteger statusCode = _statusCode;
  104. BOOL completed = [reader
  105. readAllPartsWithCompletionCallback:^(NSDictionary *headers, NSData *content, BOOL done) {
  106. partHandler(statusCode, headers, content, nil, done);
  107. }
  108. progressCallback:_progressHandler];
  109. if (!completed) {
  110. partHandler(
  111. statusCode, nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil], YES);
  112. }
  113. }
  114. @end