diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index d24b49ce7..fd40d5a89 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -14,7 +14,6 @@ #import "RCTSourceCode.h" #import "RCTUtils.h" #import "RCTPerformanceLogger.h" -#import "RCTMultipartDataTask.h" #include @@ -152,51 +151,50 @@ static void attemptAsynchronousLoadOfBundleAtURL(NSURL *scriptURL, RCTSourceLoad return; } + // Load remote script file + NSURLSessionDataTask *task = + [[NSURLSession sharedSession] dataTaskWithURL:scriptURL completionHandler: + ^(NSData *data, NSURLResponse *response, NSError *error) { - RCTMultipartDataTask *task = [[RCTMultipartDataTask alloc] initWithURL:scriptURL partHandler:^(NSInteger statusCode, NSDictionary *headers, NSData *data, NSError *error, BOOL done) { - if (!done) { - // TODO(frantic): Emit progress event - return; - } - - // Handle general request errors - if (error) { - if ([error.domain isEqualToString:NSURLErrorDomain]) { - error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain - code:RCTJavaScriptLoaderErrorURLLoadFailed - userInfo: - @{ - NSLocalizedDescriptionKey: - [@"Could not connect to development server.\n\n" - "Ensure the following:\n" - "- Node server is running and available on the same network - run 'npm start' from react-native root\n" - "- Node server URL is correctly set in AppDelegate\n\n" - "URL: " stringByAppendingString:scriptURL.absoluteString], - NSLocalizedFailureReasonErrorKey: error.localizedDescription, - NSUnderlyingErrorKey: error, - }]; - } - onComplete(error, nil, 0); - return; - } - - // For multipart responses packager sets X-Http-Status header in case HTTP status code - // is different from 200 OK - NSString *statusCodeHeader = [headers valueForKey:@"X-Http-Status"]; - if (statusCodeHeader) { - statusCode = [statusCodeHeader integerValue]; - } - - if (statusCode != 200) { - error = [NSError errorWithDomain:@"JSServer" - code:statusCode - userInfo:userInfoForRawResponse([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding])]; - onComplete(error, nil, 0); - return; - } - onComplete(nil, data, data.length); - }]; + // Handle general request errors + if (error) { + if ([error.domain isEqualToString:NSURLErrorDomain]) { + error = [NSError errorWithDomain:RCTJavaScriptLoaderErrorDomain + code:RCTJavaScriptLoaderErrorURLLoadFailed + userInfo: + @{ + NSLocalizedDescriptionKey: + [@"Could not connect to development server.\n\n" + "Ensure the following:\n" + "- Node server is running and available on the same network - run 'npm start' from react-native root\n" + "- Node server URL is correctly set in AppDelegate\n\n" + "URL: " stringByAppendingString:scriptURL.absoluteString], + NSLocalizedFailureReasonErrorKey: error.localizedDescription, + NSUnderlyingErrorKey: error, + }]; + } + onComplete(error, nil, 0); + return; + } + // Parse response as text + NSStringEncoding encoding = NSUTF8StringEncoding; + if (response.textEncodingName != nil) { + CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName); + if (cfEncoding != kCFStringEncodingInvalidId) { + encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); + } + } + // Handle HTTP errors + if ([response isKindOfClass:[NSHTTPURLResponse class]] && ((NSHTTPURLResponse *)response).statusCode != 200) { + error = [NSError errorWithDomain:@"JSServer" + code:((NSHTTPURLResponse *)response).statusCode + userInfo:userInfoForRawResponse([[NSString alloc] initWithData:data encoding:encoding])]; + onComplete(error, nil, 0); + return; + } + onComplete(nil, data, data.length); + }]; [task resume]; } diff --git a/React/Base/RCTMultipartDataTask.h b/React/Base/RCTMultipartDataTask.h deleted file mode 100644 index 246fba1b3..000000000 --- a/React/Base/RCTMultipartDataTask.h +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import -#import "RCTMultipartStreamReader.h" - -typedef void (^RCTMultipartDataTaskCallback)(NSInteger statusCode, NSDictionary *headers, NSData *content, NSError *error, BOOL done); - -@interface RCTMultipartDataTask : NSObject - -- (instancetype)initWithURL:(NSURL *)url partHandler:(RCTMultipartDataTaskCallback)partHandler; -- (void)resume; - -@end diff --git a/React/Base/RCTMultipartDataTask.m b/React/Base/RCTMultipartDataTask.m deleted file mode 100644 index 98a341259..000000000 --- a/React/Base/RCTMultipartDataTask.m +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "RCTMultipartDataTask.h" - -@interface RCTMultipartDataTask () - -@end - -@implementation RCTMultipartDataTask { - NSURLSessionDataTask *_dataTask; - RCTMultipartDataTaskCallback _partHandler; - NSInteger _statusCode; - NSDictionary *_headers; - NSString *_boundary; - NSMutableData *_data; -} - -- (instancetype)initWithURL:(NSURL *)url partHandler:(RCTMultipartDataTaskCallback)partHandler -{ - if (self = [super init]) { - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] - delegate:self delegateQueue:nil]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [request addValue:@"multipart/mixed" forHTTPHeaderField:@"Accept"]; - _dataTask = [session dataTaskWithRequest:request]; - _partHandler = [partHandler copy]; - } - return self; -} - -- (void)resume -{ - [_dataTask resume]; -} - -- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler -{ - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - _headers = [httpResponse allHeaderFields]; - _statusCode = [httpResponse statusCode]; - - NSString *contentType = @""; - for (NSString *key in [_headers keyEnumerator]) { - if ([[key lowercaseString] isEqualToString:@"content-type"]) { - contentType = [_headers valueForKey:key]; - break; - } - } - - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"multipart/mixed;.*boundary=\"([^\"]+)\"" options:0 error:nil]; - NSTextCheckingResult *match = [regex firstMatchInString:contentType options:0 range:NSMakeRange(0, contentType.length)]; - if (match) { - _boundary = [contentType substringWithRange:[match rangeAtIndex:1]]; - completionHandler(NSURLSessionResponseBecomeStream); - return; - } - } - - // In case the server doesn't support multipart/mixed responses, fallback to normal download - _data = [[NSMutableData alloc] initWithCapacity:1024 * 1024]; - completionHandler(NSURLSessionResponseAllow); -} - -- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error -{ - _partHandler(_statusCode, _headers, _data, error, YES); -} - -- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data -{ - [_data appendData:data]; -} - -- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask -{ - [streamTask captureStreams]; -} - -- (void)URLSession:(NSURLSession *)session streamTask:(NSURLSessionStreamTask *)streamTask didBecomeInputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream -{ - RCTMultipartStreamReader *reader = [[RCTMultipartStreamReader alloc] initWithInputStream:inputStream boundary:_boundary]; - RCTMultipartDataTaskCallback partHandler = _partHandler; - NSInteger statusCode = _statusCode; - - BOOL completed = [reader readAllParts:^(NSDictionary *headers, NSData *content, BOOL done) { - partHandler(statusCode, headers, content, nil, done); - }]; - if (!completed) { - partHandler(statusCode, nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil], YES); - } -} - - -@end diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index ef1b8dcc6..bccf3cca4 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; }; 001BFCD01D8381DE008E587E /* RCTMultipartStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 001BFCCF1D8381DE008E587E /* RCTMultipartStreamReader.m */; }; - 006FC4141D9B20820057AAAD /* RCTMultipartDataTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 006FC4131D9B20820057AAAD /* RCTMultipartDataTask.m */; }; 008341F61D1DB34400876D9A /* RCTJSStackFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 008341F41D1DB34400876D9A /* RCTJSStackFrame.m */; }; 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */; }; 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */; }; @@ -220,8 +219,6 @@ 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = ""; }; 001BFCCE1D8381DE008E587E /* RCTMultipartStreamReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultipartStreamReader.h; sourceTree = ""; }; 001BFCCF1D8381DE008E587E /* RCTMultipartStreamReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultipartStreamReader.m; sourceTree = ""; }; - 006FC4121D9B20820057AAAD /* RCTMultipartDataTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultipartDataTask.h; sourceTree = ""; }; - 006FC4131D9B20820057AAAD /* RCTMultipartDataTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultipartDataTask.m; sourceTree = ""; }; 008341F41D1DB34400876D9A /* RCTJSStackFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJSStackFrame.m; sourceTree = ""; }; 008341F51D1DB34400876D9A /* RCTJSStackFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSStackFrame.h; sourceTree = ""; }; 131541CF1D3E4893006A0E08 /* CSSLayout-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSSLayout-internal.h"; sourceTree = ""; }; @@ -723,8 +720,6 @@ 14C2CA731B3AC64300E6CBB2 /* RCTModuleData.mm */, 14C2CA6F1B3AC63800E6CBB2 /* RCTModuleMethod.h */, 14C2CA701B3AC63800E6CBB2 /* RCTModuleMethod.m */, - 006FC4121D9B20820057AAAD /* RCTMultipartDataTask.h */, - 006FC4131D9B20820057AAAD /* RCTMultipartDataTask.m */, 001BFCCE1D8381DE008E587E /* RCTMultipartStreamReader.h */, 001BFCCF1D8381DE008E587E /* RCTMultipartStreamReader.m */, 13A6E20F1C19ABC700845B82 /* RCTNullability.h */, @@ -999,7 +994,6 @@ 13A0C28A1B74F71200B29F6F /* RCTDevMenu.m in Sources */, 13BCE8091C99CB9D00DD7AAD /* RCTRootShadowView.m in Sources */, 14C2CA711B3AC63800E6CBB2 /* RCTModuleMethod.m in Sources */, - 006FC4141D9B20820057AAAD /* RCTMultipartDataTask.m in Sources */, 1321C8D01D3EB50800D58318 /* CSSNodeList.c in Sources */, 13CC8A821B17642100940AE7 /* RCTBorderDrawing.m in Sources */, 83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */,