Skip to content

Commit

Permalink
Implement HostTargetDelegate.networkRequest on iOS (facebook#44846)
Browse files Browse the repository at this point in the history
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
robhogan authored and facebook-github-bot committed Aug 6, 2024
1 parent 67367a0 commit 795c045
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 2 deletions.
12 changes: 11 additions & 1 deletion packages/react-native/React/Base/RCTBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#import <jsinspector-modern/ReactCdp.h>
#import <optional>
#import "RCTDevLoadingViewProtocol.h"
#import "RCTInspectorNetworkHelper.h"
#import "RCTInspectorUtils.h"
#import "RCTJSThread.h"
#import "RCTLog.h"
Expand Down Expand Up @@ -182,7 +183,9 @@ void RCTUIManagerSetDispatchAccessibilityManagerInitOntoMain(BOOL enabled)
class RCTBridgeHostTargetDelegate : public facebook::react::jsinspector_modern::HostTargetDelegate {
public:
RCTBridgeHostTargetDelegate(RCTBridge *bridge)
: bridge_(bridge), pauseOverlayController_([[RCTPausedInDebuggerOverlayController alloc] init])
: bridge_(bridge),
pauseOverlayController_([[RCTPausedInDebuggerOverlayController alloc] init]),
networkHelper_([[RCTInspectorNetworkHelper alloc] init])
{
}

Expand Down Expand Up @@ -229,9 +232,16 @@ void onSetPausedInDebuggerMessage(const OverlaySetPausedInDebuggerMessageRequest
}
}

void loadNetworkResource(const RCTInspectorLoadNetworkResourceRequest &params, RCTInspectorNetworkExecutor executor)
override
{
[networkHelper_ loadNetworkResourceWithParams:params executor:executor];
}

private:
__weak RCTBridge *bridge_;
RCTPausedInDebuggerOverlayController *pauseOverlayController_;
RCTInspectorNetworkHelper *networkHelper_;
};

@interface RCTBridge () <RCTReloadListener>
Expand Down
25 changes: 25 additions & 0 deletions packages/react-native/React/DevSupport/RCTInspectorNetworkHelper.h
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 packages/react-native/React/DevSupport/RCTInspectorNetworkHelper.mm
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#import <React/RCTConvert.h>
#import <React/RCTFabricSurface.h>
#import <React/RCTInspectorDevServerHelper.h>
#import <React/RCTInspectorNetworkHelper.h>
#import <React/RCTInspectorUtils.h>
#import <React/RCTJSThread.h>
#import <React/RCTLog.h>
Expand All @@ -37,7 +38,9 @@ @interface RCTHost () <RCTReloadListener, RCTInstanceDelegate>
class RCTHostHostTargetDelegate : public facebook::react::jsinspector_modern::HostTargetDelegate {
public:
RCTHostHostTargetDelegate(RCTHost *host)
: host_(host), pauseOverlayController_([[RCTPausedInDebuggerOverlayController alloc] init])
: host_(host),
pauseOverlayController_([[RCTPausedInDebuggerOverlayController alloc] init]),
networkHelper_([[RCTInspectorNetworkHelper alloc] init])
{
}

Expand Down Expand Up @@ -84,9 +87,16 @@ void onSetPausedInDebuggerMessage(const OverlaySetPausedInDebuggerMessageRequest
}
}

void loadNetworkResource(const RCTInspectorLoadNetworkResourceRequest &params, RCTInspectorNetworkExecutor executor)
override
{
[networkHelper_ loadNetworkResourceWithParams:params executor:executor];
}

private:
__weak RCTHost *host_;
RCTPausedInDebuggerOverlayController *pauseOverlayController_;
RCTInspectorNetworkHelper *networkHelper_;
};

@implementation RCTHost {
Expand Down

0 comments on commit 795c045

Please sign in to comment.