forked from facebook/react-native
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement HostTargetDelegate.networkRequest on iOS (facebook#44846)
Summary: Pull Request resolved: facebook#44846 Implement the `networkRequest` method of `jsinspector_modern::HostTargetDelegate` for iOS (bridge and bridgeless) to satisfy CDP `Network.loadNetworkResource` (etc) requests. Changelog: [iOS][Added] Debugger: Implement CDP methods for loading network resources through the debug target. Reviewed By: huntie Differential Revision: D54496969
- Loading branch information
1 parent
67367a0
commit 795c045
Showing
4 changed files
with
162 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
packages/react-native/React/DevSupport/RCTInspectorNetworkHelper.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
#import <jsinspector-modern/ReactCdp.h> | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
typedef facebook::react::jsinspector_modern::NetworkRequestListener RCTInspectorNetworkListener; | ||
|
||
typedef facebook::react::jsinspector_modern::ScopedExecutor<RCTInspectorNetworkListener> RCTInspectorNetworkExecutor; | ||
|
||
typedef facebook::react::jsinspector_modern::LoadNetworkResourceRequest RCTInspectorLoadNetworkResourceRequest; | ||
|
||
/** | ||
* A helper class that wraps around NSURLSession to make network requests. | ||
*/ | ||
@interface RCTInspectorNetworkHelper : NSObject | ||
- (instancetype)init; | ||
- (void)loadNetworkResourceWithParams:(const RCTInspectorLoadNetworkResourceRequest &)params | ||
executor:(RCTInspectorNetworkExecutor)executor; | ||
@end |
115 changes: 115 additions & 0 deletions
115
packages/react-native/React/DevSupport/RCTInspectorNetworkHelper.mm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
#import "RCTInspectorNetworkHelper.h" | ||
#import <React/RCTLog.h> | ||
|
||
typedef void (^ListenerBlock)(RCTInspectorNetworkListener *); | ||
|
||
@interface RCTInspectorNetworkHelper () <NSURLSessionDataDelegate> | ||
@property (nonatomic, strong) NSURLSession *session; | ||
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, void (^)(ListenerBlock)> *executorsByTaskId; | ||
- (void)withListenerForTask:(NSURLSessionTask *)task execute:(ListenerBlock)block; | ||
@end | ||
|
||
@implementation RCTInspectorNetworkHelper | ||
|
||
- (instancetype)init | ||
{ | ||
self = [super init]; | ||
if (self) { | ||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; | ||
self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; | ||
self.executorsByTaskId = [NSMutableDictionary new]; | ||
} | ||
return self; | ||
} | ||
|
||
- (void)loadNetworkResourceWithParams:(const RCTInspectorLoadNetworkResourceRequest &)params | ||
executor:(RCTInspectorNetworkExecutor)executor | ||
{ | ||
NSString *urlString = [NSString stringWithCString:params.url.c_str() encoding:NSUTF8StringEncoding]; | ||
NSURL *url = [NSURL URLWithString:urlString]; | ||
auto executorBlock = ^(ListenerBlock func) { | ||
executor([=](RCTInspectorNetworkListener &listener) { func(&listener); }); | ||
}; | ||
|
||
if (url == nil) { | ||
executorBlock(^(RCTInspectorNetworkListener *listener) { | ||
listener->onError([NSString stringWithFormat:@"Not a valid URL: %@", urlString].UTF8String); | ||
}); | ||
return; | ||
} | ||
|
||
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; | ||
[urlRequest setHTTPMethod:@"GET"]; | ||
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:urlRequest]; | ||
__weak NSURLSessionDataTask *weakDataTask = dataTask; | ||
|
||
executorBlock(^(RCTInspectorNetworkListener *listener) { | ||
listener->setCancelFunction([weakDataTask]() { [weakDataTask cancel]; }); | ||
}); | ||
|
||
// Store the executor as a block per task. | ||
self.executorsByTaskId[@(dataTask.taskIdentifier)] = executorBlock; | ||
|
||
[dataTask resume]; | ||
} | ||
|
||
- (void)withListenerForTask:(NSURLSessionTask *)task execute:(ListenerBlock)block | ||
{ | ||
void (^executor)(ListenerBlock) = self.executorsByTaskId[@(task.taskIdentifier)]; | ||
if (executor) { | ||
executor(block); | ||
} | ||
} | ||
|
||
#pragma mark - NSURLSessionDataDelegate | ||
|
||
- (void)URLSession:(NSURLSession *)session | ||
dataTask:(NSURLSessionDataTask *)dataTask | ||
didReceiveResponse:(NSURLResponse *)response | ||
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler | ||
{ | ||
auto callbackWithHeadersOrError = (^(RCTInspectorNetworkListener *listener) { | ||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { | ||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; | ||
std::map<std::string, std::string> headersMap; | ||
for (NSString *key in httpResponse.allHeaderFields) { | ||
headersMap[[key UTF8String]] = [[httpResponse.allHeaderFields objectForKey:key] UTF8String]; | ||
} | ||
completionHandler(NSURLSessionResponseAllow); | ||
listener->onHeaders(httpResponse.statusCode, headersMap); | ||
} else { | ||
listener->onError("Unsupported response type"); | ||
completionHandler(NSURLSessionResponseCancel); | ||
} | ||
}); | ||
[self withListenerForTask:dataTask execute:callbackWithHeadersOrError]; | ||
} | ||
|
||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data | ||
{ | ||
auto callbackWithData = ^(RCTInspectorNetworkListener *listener) { | ||
listener->onData(std::string_view(static_cast<const char *>(data.bytes), data.length)); | ||
}; | ||
[self withListenerForTask:dataTask execute:callbackWithData]; | ||
} | ||
|
||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error | ||
{ | ||
auto callbackWithCompletionOrError = ^(RCTInspectorNetworkListener *listener) { | ||
if (error != nil) { | ||
listener->onError(error.localizedDescription.UTF8String); | ||
} else { | ||
listener->onCompletion(); | ||
} | ||
}; | ||
[self withListenerForTask:task execute:callbackWithCompletionOrError]; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters