mirror of
https://github.com/status-im/react-native.git
synced 2025-02-25 15:45:32 +00:00
Refactored networking logic out into RCTDownloadTask
This commit is contained in:
parent
6fea7c2846
commit
9d19829742
40
Libraries/Network/RCTDownloadTask.h
Normal file
40
Libraries/Network/RCTDownloadTask.h
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTURLRequestDelegate.h"
|
||||
#import "RCTURLRequestHandler.h"
|
||||
|
||||
typedef void (^RCTURLRequestCompletionBlock)(NSURLResponse *response, NSData *data, NSError *error);
|
||||
typedef void (^RCTURLRequestCancellationBlock)(void);
|
||||
typedef void (^RCTURLRequestIncrementalDataBlock)(NSData *data);
|
||||
typedef void (^RCTURLRequestProgressBlock)(double progress, double total);
|
||||
typedef void (^RCTURLRequestResponseBlock)(NSURLResponse *response);
|
||||
|
||||
@interface RCTDownloadTask : NSObject <RCTURLRequestDelegate>
|
||||
|
||||
@property (nonatomic, readonly) NSURLRequest *request;
|
||||
@property (nonatomic, readonly) NSNumber *requestID;
|
||||
@property (nonatomic, readonly) id requestToken;
|
||||
@property (nonatomic, readonly) NSURLResponse *response;
|
||||
@property (nonatomic, readonly) RCTURLRequestCompletionBlock completionBlock;
|
||||
|
||||
@property (nonatomic, copy) RCTURLRequestProgressBlock downloadProgressBlock;
|
||||
@property (nonatomic, copy) RCTURLRequestIncrementalDataBlock incrementalDataBlock;
|
||||
@property (nonatomic, copy) RCTURLRequestResponseBlock responseBlock;
|
||||
@property (nonatomic, copy) RCTURLRequestProgressBlock uploadProgressBlock;
|
||||
|
||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
||||
handler:(id<RCTURLRequestHandler>)handler
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)cancel;
|
||||
|
||||
@end
|
102
Libraries/Network/RCTDownloadTask.m
Normal file
102
Libraries/Network/RCTDownloadTask.m
Normal file
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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 "RCTDownloadTask.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
|
||||
@implementation RCTDownloadTask
|
||||
{
|
||||
NSMutableData *_data;
|
||||
id<RCTURLRequestHandler> _handler;
|
||||
RCTDownloadTask *_selfReference;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
||||
handler:(id<RCTURLRequestHandler>)handler
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
|
||||
{
|
||||
RCTAssertParam(request);
|
||||
RCTAssertParam(handler);
|
||||
RCTAssertParam(completionBlock);
|
||||
|
||||
static NSUInteger requestID = 0;
|
||||
|
||||
if ((self = [super init])) {
|
||||
if (!(_requestToken = [handler sendRequest:request withDelegate:self])) {
|
||||
return nil;
|
||||
}
|
||||
_requestID = @(requestID++);
|
||||
_request = request;
|
||||
_handler = handler;
|
||||
_completionBlock = completionBlock;
|
||||
_selfReference = self;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
_selfReference = nil;
|
||||
_completionBlock = nil;
|
||||
_downloadProgressBlock = nil;
|
||||
_incrementalDataBlock = nil;
|
||||
_responseBlock = nil;
|
||||
_uploadProgressBlock = nil;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(-init)
|
||||
|
||||
- (void)cancel
|
||||
{
|
||||
if ([_handler respondsToSelector:@selector(cancelRequest:)]) {
|
||||
[_handler cancelRequest:_requestToken];
|
||||
}
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didSendDataWithProgress:(int64_t)bytesSent
|
||||
{
|
||||
RCTAssert([requestToken isEqual:_requestToken], @"Unrecognized request token: %@", requestToken);
|
||||
if (_uploadProgressBlock) {
|
||||
_uploadProgressBlock(bytesSent, _request.HTTPBody.length);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response
|
||||
{
|
||||
RCTAssert([requestToken isEqual:_requestToken], @"Unrecognized request token: %@", requestToken);
|
||||
_response = response;
|
||||
if (_responseBlock) {
|
||||
_responseBlock(response);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didReceiveData:(NSData *)data
|
||||
{
|
||||
RCTAssert([requestToken isEqual:_requestToken], @"Unrecognized request token: %@", requestToken);
|
||||
if (!_data) {
|
||||
_data = [[NSMutableData alloc] init];
|
||||
}
|
||||
[_data appendData:data];
|
||||
if (_incrementalDataBlock) {
|
||||
_incrementalDataBlock(data);
|
||||
}
|
||||
if (_downloadProgressBlock && _response.expectedContentLength > 0) {
|
||||
_downloadProgressBlock(_data.length, _response.expectedContentLength);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didCompleteWithError:(NSError *)error
|
||||
{
|
||||
_completionBlock(_response, _data, error);
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
@end
|
@ -10,6 +10,9 @@
|
||||
#import "RCTURLRequestHandler.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
/**
|
||||
* This is the default RCTURLRequestHandler implementation for HTTP requests.
|
||||
*/
|
||||
@interface RCTHTTPRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
|
||||
|
||||
@end
|
||||
|
@ -50,8 +50,8 @@ RCT_EXPORT_MODULE()
|
||||
return [@[@"http", @"https", @"file"] containsObject:[request.URL.scheme lowercaseString]];
|
||||
}
|
||||
|
||||
- (id)sendRequest:(NSURLRequest *)request
|
||||
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||
- (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request
|
||||
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||
{
|
||||
// Lazy setup
|
||||
if (!_session && [self isValid]) {
|
||||
@ -68,9 +68,10 @@ RCT_EXPORT_MODULE()
|
||||
return task;
|
||||
}
|
||||
|
||||
- (void)cancelRequest:(NSURLSessionDataTask *)requestToken
|
||||
- (void)cancelRequest:(NSURLSessionDataTask *)task
|
||||
{
|
||||
[requestToken cancel];
|
||||
[task cancel];
|
||||
[_delegates removeObjectForKey:task];
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSession delegate
|
||||
@ -81,10 +82,9 @@ RCT_EXPORT_MODULE()
|
||||
totalBytesSent:(int64_t)totalBytesSent
|
||||
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
|
||||
{
|
||||
[[_delegates objectForKey:task] URLRequest:task didUploadProgress:(double)totalBytesSent total:(double)totalBytesExpectedToSend];
|
||||
[[_delegates objectForKey:task] URLRequest:task didSendDataWithProgress:totalBytesSent];
|
||||
}
|
||||
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
dataTask:(NSURLSessionDataTask *)task
|
||||
didReceiveResponse:(NSURLResponse *)response
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1372B7371AB03E7B00659ED6 /* RCTReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTReachability.m */; };
|
||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */; };
|
||||
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */; };
|
||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
@ -27,6 +28,8 @@
|
||||
/* Begin PBXFileReference section */
|
||||
1372B7351AB03E7B00659ED6 /* RCTReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTReachability.h; sourceTree = "<group>"; };
|
||||
1372B7361AB03E7B00659ED6 /* RCTReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTReachability.m; sourceTree = "<group>"; };
|
||||
13D6D6681B5FCF8200883BE9 /* RCTDownloadTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTask.h; sourceTree = "<group>"; };
|
||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTask.m; sourceTree = "<group>"; };
|
||||
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHTTPRequestHandler.h; sourceTree = "<group>"; };
|
||||
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTHTTPRequestHandler.m; sourceTree = "<group>"; };
|
||||
58B511DB1A9E6C8500147676 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -48,6 +51,8 @@
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13D6D6681B5FCF8200883BE9 /* RCTDownloadTask.h */,
|
||||
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */,
|
||||
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
|
||||
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */,
|
||||
58B512061A9E6CE300147676 /* RCTNetworking.h */,
|
||||
@ -124,6 +129,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */,
|
||||
1372B7371AB03E7B00659ED6 /* RCTReachability.m in Sources */,
|
||||
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */,
|
||||
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */,
|
||||
|
@ -14,4 +14,3 @@
|
||||
@interface RCTNetworking : NSObject <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
|
||||
|
@ -11,18 +11,19 @@
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTDownloadTask.h"
|
||||
#import "RCTURLRequestHandler.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTHTTPRequestHandler.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
typedef void (^RCTHTTPQueryResult)(NSError *error, NSDictionary *result);
|
||||
typedef RCTURLRequestCancellationBlock (^RCTHTTPQueryResult)(NSError *error, NSDictionary *result);
|
||||
|
||||
@interface RCTNetworking ()<RCTURLRequestDelegate>
|
||||
|
||||
- (void)processDataForHTTPQuery:(NSDictionary *)data callback:(void (^)(NSError *error, NSDictionary *result))callback;
|
||||
@interface RCTNetworking ()
|
||||
|
||||
- (RCTURLRequestCancellationBlock)processDataForHTTPQuery:(NSDictionary *)data
|
||||
callback:(RCTHTTPQueryResult)callback;
|
||||
@end
|
||||
|
||||
/**
|
||||
@ -30,7 +31,7 @@ typedef void (^RCTHTTPQueryResult)(NSError *error, NSDictionary *result);
|
||||
*/
|
||||
@interface RCTHTTPFormDataHelper : NSObject
|
||||
|
||||
@property (nonatomic, weak) RCTNetworking *dataManager;
|
||||
@property (nonatomic, weak) RCTNetworking *networker;
|
||||
|
||||
@end
|
||||
|
||||
@ -42,51 +43,49 @@ typedef void (^RCTHTTPQueryResult)(NSError *error, NSDictionary *result);
|
||||
NSString *boundary;
|
||||
}
|
||||
|
||||
- (void)process:(NSArray *)formData callback:(void (^)(NSError *error, NSDictionary *result))callback
|
||||
static NSString *RCTGenerateFormBoundary()
|
||||
{
|
||||
if (![formData count]) {
|
||||
callback(nil, nil);
|
||||
return;
|
||||
const size_t boundaryLength = 70;
|
||||
const char *boundaryChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_./";
|
||||
|
||||
char *bytes = malloc(boundaryLength);
|
||||
size_t charCount = strlen(boundaryChars);
|
||||
for (int i = 0; i < boundaryLength; i++) {
|
||||
bytes[i] = boundaryChars[arc4random_uniform((u_int32_t)charCount)];
|
||||
}
|
||||
return [[NSString alloc] initWithBytesNoCopy:bytes length:boundaryLength encoding:NSUTF8StringEncoding freeWhenDone:YES];
|
||||
}
|
||||
|
||||
- (RCTURLRequestCancellationBlock)process:(NSArray *)formData
|
||||
callback:(RCTHTTPQueryResult)callback
|
||||
{
|
||||
if (formData.count == 0) {
|
||||
return callback(nil, nil);
|
||||
}
|
||||
|
||||
parts = [formData mutableCopy];
|
||||
_callback = callback;
|
||||
multipartBody = [[NSMutableData alloc] init];
|
||||
boundary = [self generateBoundary];
|
||||
boundary = RCTGenerateFormBoundary();
|
||||
|
||||
NSDictionary *currentPart = [parts objectAtIndex: 0];
|
||||
[_dataManager processDataForHTTPQuery:currentPart callback:^(NSError *e, NSDictionary *r) {
|
||||
[self handleResult:r error:e];
|
||||
return [_networker processDataForHTTPQuery:parts[0] callback:^(NSError *error, NSDictionary *result) {
|
||||
return [self handleResult:result error:error];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)generateBoundary
|
||||
{
|
||||
NSString *const boundaryChars = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_./";
|
||||
const NSUInteger boundaryLength = 70;
|
||||
|
||||
NSMutableString *output = [NSMutableString stringWithCapacity:boundaryLength];
|
||||
NSUInteger numchars = [boundaryChars length];
|
||||
for (NSUInteger i = 0; i < boundaryLength; i++) {
|
||||
[output appendFormat:@"%C", [boundaryChars characterAtIndex:arc4random_uniform((u_int32_t)numchars)]];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
- (void)handleResult:(NSDictionary *)result error:(NSError *)error
|
||||
- (RCTURLRequestCancellationBlock)handleResult:(NSDictionary *)result
|
||||
error:(NSError *)error
|
||||
{
|
||||
if (error) {
|
||||
_callback(error, nil);
|
||||
return;
|
||||
return _callback(error, nil);
|
||||
}
|
||||
NSDictionary *currentPart = parts[0];
|
||||
[parts removeObjectAtIndex:0];
|
||||
|
||||
// Start with boundary.
|
||||
[multipartBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
// Print headers.
|
||||
NSMutableDictionary *headers = [(NSDictionary*)currentPart[@"headers"] mutableCopy];
|
||||
NSMutableDictionary *headers = [parts[0][@"headers"] mutableCopy];
|
||||
NSString *partContentType = result[@"contentType"];
|
||||
if (partContentType != nil) {
|
||||
[headers setObject:partContentType forKey:@"content-type"];
|
||||
@ -101,110 +100,18 @@ typedef void (^RCTHTTPQueryResult)(NSError *error, NSDictionary *result);
|
||||
[multipartBody appendData:result[@"body"]];
|
||||
[multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
if ([parts count]) {
|
||||
NSDictionary *nextPart = [parts objectAtIndex: 0];
|
||||
[_dataManager processDataForHTTPQuery:nextPart callback:^(NSError *e, NSDictionary *r) {
|
||||
[self handleResult:r error:e];
|
||||
[parts removeObjectAtIndex:0];
|
||||
if (parts.count) {
|
||||
return [_networker processDataForHTTPQuery:parts[0] callback:^(NSError *err, NSDictionary *res) {
|
||||
return [self handleResult:res error:err];
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
// We've processed the last item. Finish and return.
|
||||
[multipartBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"", boundary];
|
||||
_callback(nil, @{@"body": multipartBody, @"contentType": contentType});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Helper to package in-flight requests together with their response data.
|
||||
*/
|
||||
@interface RCTActiveURLRequest : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSNumber *requestID;
|
||||
@property (nonatomic, strong) NSURLRequest *request;
|
||||
@property (nonatomic, strong) id<RCTURLRequestHandler> handler;
|
||||
@property (nonatomic, assign) BOOL incrementalUpdates;
|
||||
@property (nonatomic, strong) NSURLResponse *response;
|
||||
@property (nonatomic, strong) NSMutableData *data;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTActiveURLRequest
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_data = [[NSMutableData alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Helper to load request body data using a handler.
|
||||
*/
|
||||
@interface RCTDataLoader : NSObject <RCTURLRequestDelegate>
|
||||
|
||||
@end
|
||||
|
||||
typedef void (^RCTDataLoaderCallback)(NSData *data, NSString *MIMEType, NSError *error);
|
||||
|
||||
@implementation RCTDataLoader
|
||||
{
|
||||
RCTDataLoaderCallback _callback;
|
||||
RCTActiveURLRequest *_request;
|
||||
id _requestToken;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
||||
handler:(id<RCTURLRequestHandler>)handler
|
||||
callback:(RCTDataLoaderCallback)callback
|
||||
{
|
||||
RCTAssertParam(request);
|
||||
RCTAssertParam(handler);
|
||||
RCTAssertParam(callback);
|
||||
|
||||
if ((self = [super init])) {
|
||||
_callback = callback;
|
||||
_request = [[RCTActiveURLRequest alloc] init];
|
||||
_request.request = request;
|
||||
_request.handler = handler;
|
||||
_request.incrementalUpdates = NO;
|
||||
_requestToken = [handler sendRequest:request withDelegate:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithRequest:nil handler:nil callback:nil];
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didUploadProgress:(double)progress total:(double)total
|
||||
{
|
||||
RCTAssert([requestToken isEqual:_requestToken], @"Shouldn't ever happen");
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response
|
||||
{
|
||||
RCTAssert([requestToken isEqual:_requestToken], @"Shouldn't ever happen");
|
||||
_request.response = response;
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didReceiveData:(NSData *)data
|
||||
{
|
||||
RCTAssert([requestToken isEqual:_requestToken], @"Shouldn't ever happen");
|
||||
[_request.data appendData:data];
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didCompleteWithError:(NSError *)error
|
||||
{
|
||||
RCTAssert(_callback != nil, @"The callback property must be set");
|
||||
_callback(_request.data, _request.response.MIMEType, error);
|
||||
return _callback(nil, @{@"body": multipartBody, @"contentType": contentType});
|
||||
}
|
||||
|
||||
@end
|
||||
@ -214,8 +121,7 @@ typedef void (^RCTDataLoaderCallback)(NSData *data, NSString *MIMEType, NSError
|
||||
*/
|
||||
@implementation RCTNetworking
|
||||
{
|
||||
NSInteger _currentRequestID;
|
||||
NSMapTable *_activeRequests;
|
||||
NSMutableDictionary *_tasksByRequestID;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
@ -226,16 +132,13 @@ RCT_EXPORT_MODULE()
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_currentRequestID = 0;
|
||||
_activeRequests = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
|
||||
valueOptions:NSPointerFunctionsStrongMemory
|
||||
capacity:0];
|
||||
_tasksByRequestID = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)buildRequest:(NSDictionary *)query
|
||||
completionBlock:(void (^)(NSURLRequest *request))block
|
||||
- (RCTURLRequestCancellationBlock)buildRequest:(NSDictionary *)query
|
||||
completionBlock:(void (^)(NSURLRequest *request))block
|
||||
{
|
||||
NSURL *URL = [RCTConvert NSURL:query[@"url"]];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
|
||||
@ -243,11 +146,11 @@ RCT_EXPORT_MODULE()
|
||||
request.allHTTPHeaderFields = [RCTConvert NSDictionary:query[@"headers"]];
|
||||
|
||||
NSDictionary *data = [RCTConvert NSDictionary:query[@"data"]];
|
||||
[self processDataForHTTPQuery:data callback:^(NSError *error, NSDictionary *result) {
|
||||
return [self processDataForHTTPQuery:data callback:^(NSError *error, NSDictionary *result) {
|
||||
if (error) {
|
||||
RCTLogError(@"Error processing request body: %@", error);
|
||||
// Ideally we'd circle back to JS here and notify an error/abort on the request.
|
||||
return;
|
||||
return (RCTURLRequestCancellationBlock)nil;
|
||||
}
|
||||
request.HTTPBody = result[@"body"];
|
||||
NSString *contentType = result[@"contentType"];
|
||||
@ -262,6 +165,7 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
|
||||
block(request);
|
||||
return (RCTURLRequestCancellationBlock)nil;
|
||||
}];
|
||||
}
|
||||
|
||||
@ -316,71 +220,56 @@ RCT_EXPORT_MODULE()
|
||||
* - @"contentType" (NSString): the content type header of the request
|
||||
*
|
||||
*/
|
||||
- (void)processDataForHTTPQuery:(NSDictionary *)query callback:(void (^)(NSError *error, NSDictionary *result))callback
|
||||
- (RCTURLRequestCancellationBlock)processDataForHTTPQuery:(NSDictionary *)query callback:
|
||||
(RCTURLRequestCancellationBlock (^)(NSError *error, NSDictionary *result))callback
|
||||
{
|
||||
if (!query) {
|
||||
callback(nil, nil);
|
||||
return;
|
||||
return callback(nil, nil);
|
||||
}
|
||||
NSData *body = [RCTConvert NSData:query[@"string"]];
|
||||
if (body) {
|
||||
callback(nil, @{@"body": body});
|
||||
return;
|
||||
return callback(nil, @{@"body": body});
|
||||
}
|
||||
NSURLRequest *request = [RCTConvert NSURLRequest:query[@"uri"]];
|
||||
if (request) {
|
||||
|
||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||
if (!handler) {
|
||||
return;
|
||||
return callback(nil, nil);
|
||||
}
|
||||
(void)[[RCTDataLoader alloc] initWithRequest:request handler:handler callback:^(NSData *data, NSString *MIMEType, NSError *error) {
|
||||
if (data) {
|
||||
callback(nil, @{@"body": data, @"contentType": MIMEType});
|
||||
} else {
|
||||
callback(error, nil);
|
||||
}
|
||||
|
||||
__block RCTURLRequestCancellationBlock cancellationBlock = nil;
|
||||
RCTDownloadTask *task = [[RCTDownloadTask alloc] initWithRequest:request handler:handler completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
cancellationBlock = callback(error, data ? @{@"body": data, @"contentType": RCTNullIfNil(response.MIMEType)} : nil);
|
||||
}];
|
||||
return;
|
||||
|
||||
__weak RCTDownloadTask *weakTask = task;
|
||||
return ^{
|
||||
[weakTask cancel];
|
||||
if (cancellationBlock) {
|
||||
cancellationBlock();
|
||||
}
|
||||
};
|
||||
}
|
||||
NSDictionaryArray *formData = [RCTConvert NSDictionaryArray:query[@"formData"]];
|
||||
if (formData != nil) {
|
||||
if (formData) {
|
||||
RCTHTTPFormDataHelper *formDataHelper = [[RCTHTTPFormDataHelper alloc] init];
|
||||
formDataHelper.dataManager = self;
|
||||
[formDataHelper process:formData callback:callback];
|
||||
return;
|
||||
formDataHelper.networker = self;
|
||||
return [formDataHelper process:formData callback:callback];
|
||||
}
|
||||
// Nothing in the data payload, at least nothing we could understand anyway.
|
||||
// Ignore and treat it as if it were null.
|
||||
callback(nil, nil);
|
||||
return callback(nil, nil);
|
||||
}
|
||||
|
||||
- (void)sendRequest:(NSURLRequest *)request
|
||||
incrementalUpdates:(BOOL)incrementalUpdates
|
||||
responseSender:(RCTResponseSenderBlock)responseSender
|
||||
{
|
||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||
id token = [handler sendRequest:request withDelegate:self];
|
||||
if (token) {
|
||||
RCTActiveURLRequest *activeRequest = [[RCTActiveURLRequest alloc] init];
|
||||
activeRequest.requestID = @(++_currentRequestID);
|
||||
activeRequest.request = request;
|
||||
activeRequest.handler = handler;
|
||||
activeRequest.incrementalUpdates = incrementalUpdates;
|
||||
[_activeRequests setObject:activeRequest forKey:token];
|
||||
responseSender(@[activeRequest.requestID]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendData:(NSData *)data forRequestToken:(id)requestToken
|
||||
- (void)sendData:(NSData *)data forTask:(RCTDownloadTask *)task
|
||||
{
|
||||
if (data.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken];
|
||||
|
||||
// Get text encoding
|
||||
NSURLResponse *response = request.response;
|
||||
NSURLResponse *response = task.response;
|
||||
NSStringEncoding encoding = NSUTF8StringEncoding;
|
||||
if (response.textEncodingName) {
|
||||
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
@ -393,82 +282,81 @@ RCT_EXPORT_MODULE()
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *responseJSON = @[request.requestID, responseText ?: @""];
|
||||
NSArray *responseJSON = @[task.requestID, responseText ?: @""];
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkData"
|
||||
body:responseJSON];
|
||||
}
|
||||
|
||||
#pragma mark - RCTURLRequestDelegate
|
||||
|
||||
- (void)URLRequest:(id)requestToken didUploadProgress:(double)progress total:(double)total
|
||||
- (void)sendRequest:(NSURLRequest *)request
|
||||
incrementalUpdates:(BOOL)incrementalUpdates
|
||||
responseSender:(RCTResponseSenderBlock)responseSender
|
||||
{
|
||||
dispatch_async(_methodQueue, ^{
|
||||
RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken];
|
||||
RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken);
|
||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *responseJSON = @[request.requestID, @(progress), @(total)];
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didUploadProgress" body:responseJSON];
|
||||
});
|
||||
}
|
||||
__block RCTDownloadTask *task;
|
||||
|
||||
- (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response
|
||||
{
|
||||
dispatch_async(_methodQueue, ^{
|
||||
RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken];
|
||||
RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken);
|
||||
RCTURLRequestProgressBlock uploadProgressBlock = ^(double progress, double total) {
|
||||
dispatch_async(_methodQueue, ^{
|
||||
NSArray *responseJSON = @[task.requestID, @(progress), @(total)];
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didSendNetworkData" body:responseJSON];
|
||||
});
|
||||
};
|
||||
|
||||
request.response = response;
|
||||
void (^responseBlock)(NSURLResponse *) = ^(NSURLResponse *response) {
|
||||
dispatch_async(_methodQueue, ^{
|
||||
NSHTTPURLResponse *httpResponse = nil;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
// Might be a local file request
|
||||
httpResponse = (NSHTTPURLResponse *)response;
|
||||
}
|
||||
NSArray *responseJSON = @[task.requestID,
|
||||
@(httpResponse.statusCode ?: 200),
|
||||
httpResponse.allHeaderFields ?: @{},
|
||||
];
|
||||
|
||||
NSHTTPURLResponse *httpResponse = nil;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
// Might be a local file request
|
||||
httpResponse = (NSHTTPURLResponse *)response;
|
||||
}
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse"
|
||||
body:responseJSON];
|
||||
});
|
||||
};
|
||||
|
||||
NSArray *responseJSON = @[request.requestID,
|
||||
@(httpResponse.statusCode ?: 200),
|
||||
httpResponse.allHeaderFields ?: @{},
|
||||
];
|
||||
void (^incrementalDataBlock)(NSData *) = incrementalUpdates ? ^(NSData *data) {
|
||||
dispatch_async(_methodQueue, ^{
|
||||
[self sendData:data forTask:task];
|
||||
});
|
||||
} : nil;
|
||||
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse"
|
||||
body:responseJSON];
|
||||
});
|
||||
}
|
||||
RCTURLRequestCompletionBlock completionBlock =
|
||||
^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
dispatch_async(_methodQueue, ^{
|
||||
if (!incrementalUpdates) {
|
||||
[self sendData:data forTask:task];
|
||||
}
|
||||
NSArray *responseJSON = @[task.requestID,
|
||||
RCTNullIfNil(error.localizedDescription),
|
||||
];
|
||||
|
||||
- (void)URLRequest:(id)requestToken didReceiveData:(NSData *)data
|
||||
{
|
||||
dispatch_async(_methodQueue, ^{
|
||||
RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken];
|
||||
RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken);
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didCompleteNetworkResponse"
|
||||
body:responseJSON];
|
||||
|
||||
if (request.incrementalUpdates) {
|
||||
[self sendData:data forRequestToken:requestToken];
|
||||
} else {
|
||||
[request.data appendData:data];
|
||||
}
|
||||
});
|
||||
}
|
||||
[_tasksByRequestID removeObjectForKey:task.requestID];
|
||||
});
|
||||
};
|
||||
|
||||
- (void)URLRequest:(id)requestToken didCompleteWithError:(NSError *)error
|
||||
{
|
||||
dispatch_async(_methodQueue, ^{
|
||||
RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken];
|
||||
RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken);
|
||||
task = [[RCTDownloadTask alloc] initWithRequest:request
|
||||
handler:handler
|
||||
completionBlock:completionBlock];
|
||||
|
||||
if (!request.incrementalUpdates) {
|
||||
[self sendData:request.data forRequestToken:requestToken];
|
||||
}
|
||||
task.incrementalDataBlock = incrementalDataBlock;
|
||||
task.responseBlock = responseBlock;
|
||||
task.uploadProgressBlock = uploadProgressBlock;
|
||||
|
||||
NSArray *responseJSON = @[
|
||||
request.requestID,
|
||||
RCTNullIfNil(error.localizedDescription),
|
||||
];
|
||||
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"didCompleteNetworkResponse"
|
||||
body:responseJSON];
|
||||
|
||||
[_activeRequests removeObjectForKey:requestToken];
|
||||
});
|
||||
if (task.requestID) {
|
||||
_tasksByRequestID[task.requestID] = task;
|
||||
responseSender(@[task.requestID]);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - JS API
|
||||
@ -476,31 +364,22 @@ RCT_EXPORT_MODULE()
|
||||
RCT_EXPORT_METHOD(sendRequest:(NSDictionary *)query
|
||||
responseSender:(RCTResponseSenderBlock)responseSender)
|
||||
{
|
||||
// TODO: buildRequest returns a cancellation block, but there's currently
|
||||
// no way to invoke it, if, for example the request is cancelled while
|
||||
// loading a large file to build the request body
|
||||
[self buildRequest:query completionBlock:^(NSURLRequest *request) {
|
||||
|
||||
BOOL incrementalUpdates = [RCTConvert BOOL:query[@"incrementalUpdates"]];
|
||||
[self sendRequest:request incrementalUpdates:incrementalUpdates
|
||||
[self sendRequest:request
|
||||
incrementalUpdates:incrementalUpdates
|
||||
responseSender:responseSender];
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(cancelRequest:(NSNumber *)requestID)
|
||||
{
|
||||
id requestToken = nil;
|
||||
RCTActiveURLRequest *activeRequest = nil;
|
||||
for (id token in _activeRequests) {
|
||||
RCTActiveURLRequest *request = [_activeRequests objectForKey:token];
|
||||
if ([request.requestID isEqualToNumber:requestID]) {
|
||||
activeRequest = request;
|
||||
requestToken = token;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
id<RCTURLRequestHandler> handler = activeRequest.handler;
|
||||
if ([handler respondsToSelector:@selector(cancelRequest:)]) {
|
||||
[activeRequest.handler cancelRequest:requestToken];
|
||||
}
|
||||
[_tasksByRequestID[requestID] cancel];
|
||||
[_tasksByRequestID removeObjectForKey:requestID];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -35,7 +35,7 @@ class XMLHttpRequest extends XMLHttpRequestBase {
|
||||
_didCreateRequest(requestId: number): void {
|
||||
this._requestId = requestId;
|
||||
this._subscriptions.push(RCTDeviceEventEmitter.addListener(
|
||||
'didUploadProgress',
|
||||
'didSendNetworkData',
|
||||
(args) => this._didUploadProgress.call(this, args[0], args[1], args[2])
|
||||
));
|
||||
this._subscriptions.push(RCTDeviceEventEmitter.addListener(
|
||||
|
@ -16,10 +16,10 @@
|
||||
@protocol RCTURLRequestDelegate <NSObject>
|
||||
|
||||
/**
|
||||
* Call this when you first receives a response from the server. This should
|
||||
* include response headers, etc.
|
||||
* Call this when you send request data to the server. This is used to track
|
||||
* upload progress, so should be called multiple times for large request bodies.
|
||||
*/
|
||||
- (void)URLRequest:(id)requestToken didUploadProgress:(double)progress total:(double)total;
|
||||
- (void)URLRequest:(id)requestToken didSendDataWithProgress:(int64_t)bytesSent;
|
||||
|
||||
/**
|
||||
* Call this when you first receives a response from the server. This should
|
||||
|
Loading…
x
Reference in New Issue
Block a user