diff --git a/packages/react-native/Libraries/Blob/RCTBlobManager.mm b/packages/react-native/Libraries/Blob/RCTBlobManager.mm index 82c08978904..286710d6910 100755 --- a/packages/react-native/Libraries/Blob/RCTBlobManager.mm +++ b/packages/react-native/Libraries/Blob/RCTBlobManager.mm @@ -159,11 +159,11 @@ RCT_EXPORT_METHOD(addNetworkingHandler) // TODO(T63516227): Why can methodQueue be nil here? // We don't want to do anything when methodQueue is nil. - if (!networking.methodQueue) { + if (![networking requestQueue]) { return; } - dispatch_async(networking.methodQueue, ^{ + dispatch_async([networking requestQueue], ^{ [networking addRequestHandler:self]; [networking addResponseHandler:self]; }); diff --git a/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm b/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm index 8f21bf3e284..4a0353396d7 100644 --- a/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm +++ b/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm @@ -78,7 +78,8 @@ RCT_EXPORT_MODULE() NSOperationQueue *callbackQueue = [NSOperationQueue new]; callbackQueue.maxConcurrentOperationCount = 1; - callbackQueue.underlyingQueue = [[_moduleRegistry moduleForName:"Networking"] methodQueue]; + RCTNetworking *networking = [_moduleRegistry moduleForName:"Networking"]; + callbackQueue.underlyingQueue = [networking requestQueue]; NSURLSessionConfiguration *configuration; if (urlSessionConfigurationProvider) { configuration = urlSessionConfigurationProvider(); diff --git a/packages/react-native/Libraries/Network/RCTNetworking.h b/packages/react-native/Libraries/Network/RCTNetworking.h index b4507aad334..249259bda21 100644 --- a/packages/react-native/Libraries/Network/RCTNetworking.h +++ b/packages/react-native/Libraries/Network/RCTNetworking.h @@ -10,6 +10,8 @@ #import #import +RCT_EXTERN void RCTEnableNetworkingRequestQueue(BOOL enabled); + @protocol RCTNetworkingRequestHandler // @lint-ignore FBOBJCUNTYPEDCOLLECTION1 @@ -56,6 +58,8 @@ - (void)removeResponseHandler:(id)handler; +- (dispatch_queue_t)requestQueue; + @end @interface RCTBridge (RCTNetworking) diff --git a/packages/react-native/Libraries/Network/RCTNetworking.mm b/packages/react-native/Libraries/Network/RCTNetworking.mm index 6e0884dc241..ae78fdfb0d9 100644 --- a/packages/react-native/Libraries/Network/RCTNetworking.mm +++ b/packages/react-native/Libraries/Network/RCTNetworking.mm @@ -19,6 +19,13 @@ #import "RCTNetworkPlugins.h" +static BOOL gEnableNetworkingRequestQueue = NO; + +RCT_EXTERN void RCTEnableNetworkingRequestQueue(BOOL enabled) +{ + gEnableNetworkingRequestQueue = enabled; +} + typedef RCTURLRequestCancellationBlock (^RCTHTTPQueryResult)(NSError *error, NSDictionary *result); NSString *const RCTNetworkingPHUploadHackScheme = @"ph-upload"; @@ -69,7 +76,7 @@ static NSString *RCTGenerateFormBoundary() - (RCTURLRequestCancellationBlock)process:(NSArray *)formData callback:(RCTHTTPQueryResult)callback { - RCTAssertThread(_networker.methodQueue, @"process: must be called on method queue"); + RCTAssertThread([_networker requestQueue], @"process: must be called on request queue"); if (formData.count == 0) { return callback(nil, nil); @@ -98,7 +105,7 @@ static NSString *RCTGenerateFormBoundary() - (RCTURLRequestCancellationBlock)handleResult:(NSDictionary *)result error:(NSError *)error { - RCTAssertThread(_networker.methodQueue, @"handleResult: must be called on method queue"); + RCTAssertThread([_networker requestQueue], @"handleResult: must be called on request queue"); if (error) { return _callback(error, nil); @@ -151,6 +158,7 @@ static NSString *RCTGenerateFormBoundary() NSArray> * (^_handlersProvider)(RCTModuleRegistry *); NSMutableArray> *_requestHandlers; NSMutableArray> *_responseHandlers; + dispatch_queue_t _requestQueue; } @synthesize methodQueue = _methodQueue; @@ -164,7 +172,12 @@ RCT_EXPORT_MODULE() - (instancetype)init { - return [super initWithDisabledObservation]; + if (self = [super initWithDisabledObservation]) { + if (gEnableNetworkingRequestQueue) { + _requestQueue = dispatch_queue_create("com.facebook.react.network.request", DISPATCH_QUEUE_SERIAL); + } + } + return self; } - (instancetype)initWithHandlersProvider: @@ -294,7 +307,7 @@ RCT_EXPORT_MODULE() - (RCTURLRequestCancellationBlock)buildRequest:(NSDictionary *)query completionBlock:(void (^)(NSURLRequest *request))block { - RCTAssertThread(_methodQueue, @"buildRequest: must be called on method queue"); + RCTAssertThread([self requestQueue], @"buildRequest: must be called on request queue"); NSURL *URL = [RCTConvert NSURL:query[@"url"]]; // this is marked as nullable in JS, but should not be null NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; @@ -347,7 +360,7 @@ RCT_EXPORT_MODULE() forHTTPHeaderField:@"Content-Length"]; } - dispatch_async(self->_methodQueue, ^{ + dispatch_async([self requestQueue], ^{ block(request); }); @@ -385,7 +398,7 @@ RCT_EXPORT_MODULE() callback:(RCTURLRequestCancellationBlock (^)(NSError *error, NSDictionary *result)) callback { - RCTAssertThread(_methodQueue, @"processDataForHTTPQuery: must be called on method queue"); + RCTAssertThread([self requestQueue], @"processDataForHTTPQuery: must be called on request queue"); if (!query) { return callback(nil, nil); @@ -414,7 +427,7 @@ RCT_EXPORT_MODULE() RCTNetworkTask *task = [self networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) { - dispatch_async(self->_methodQueue, ^{ + dispatch_async([self requestQueue], ^{ cancellationBlock = callback( error, data ? @{@"body" : data, @"contentType" : RCTNullIfNil(response.MIMEType)} : nil); }); @@ -514,7 +527,7 @@ RCT_EXPORT_MODULE() response:(NSURLResponse *)response forTask:(RCTNetworkTask *)task { - RCTAssertThread(_methodQueue, @"sendData: must be called on method queue"); + RCTAssertThread([self requestQueue], @"sendData: must be called on request queue"); id responseData = nil; for (id handler in _responseHandlers) { @@ -552,7 +565,7 @@ RCT_EXPORT_MODULE() incrementalUpdates:(BOOL)incrementalUpdates responseSender:(RCTResponseSenderBlock)responseSender { - RCTAssertThread(_methodQueue, @"sendRequest: must be called on method queue"); + RCTAssertThread([self requestQueue], @"sendRequest: must be called on request queue"); __weak __typeof(self) weakSelf = self; __block RCTNetworkTask *task; RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) { @@ -689,7 +702,9 @@ RCT_EXPORT_MODULE() return nil; } - RCTNetworkTask *task = [[RCTNetworkTask alloc] initWithRequest:request handler:handler callbackQueue:_methodQueue]; + RCTNetworkTask *task = [[RCTNetworkTask alloc] initWithRequest:request + handler:handler + callbackQueue:[self requestQueue]]; task.completionBlock = completionBlock; return task; } @@ -709,7 +724,7 @@ RCT_EXPORT_METHOD(sendRequest double timeout = query.timeout(); bool withCredentials = query.withCredentials(); - dispatch_async(_methodQueue, ^{ + dispatch_async([self requestQueue], ^{ NSDictionary *queryDict = @{ @"method" : method, @"url" : url, @@ -738,7 +753,7 @@ RCT_EXPORT_METHOD(sendRequest RCT_EXPORT_METHOD(abortRequest : (double)requestID) { - dispatch_async(_methodQueue, ^{ + dispatch_async([self requestQueue], ^{ [self->_tasksByRequestID[[NSNumber numberWithDouble:requestID]] cancel]; [self->_tasksByRequestID removeObjectForKey:[NSNumber numberWithDouble:requestID]]; }); @@ -746,7 +761,7 @@ RCT_EXPORT_METHOD(abortRequest : (double)requestID) RCT_EXPORT_METHOD(clearCookies : (RCTResponseSenderBlock)responseSender) { - dispatch_async(_methodQueue, ^{ + dispatch_async([self requestQueue], ^{ NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; if (!storage.cookies.count) { responseSender(@[ @NO ]); @@ -760,6 +775,11 @@ RCT_EXPORT_METHOD(clearCookies : (RCTResponseSenderBlock)responseSender) }); } +- (dispatch_queue_t)requestQueue +{ + return gEnableNetworkingRequestQueue ? _requestQueue : _methodQueue; +} + - (std::shared_ptr)getTurboModule: (const facebook::react::ObjCTurboModule::InitParams &)params {