Implement Network.loadingFailed (#52334)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52334

Adds support for `Network.loadingFailed` in jsinspector-modern and wires up for iOS.

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D77489477

fbshipit-source-id: dc8156979fe49583819019fa4b88b6eb99dea734
This commit is contained in:
Alex Hunt
2025-07-03 05:01:57 -07:00
committed by Facebook GitHub Bot
parent 68342a4d12
commit ebb831a0c9
7 changed files with 72 additions and 25 deletions
@@ -57,7 +57,7 @@
*
* - Corresponds to `Network.loadingFailed` in CDP.
*/
+ (void)reportRequestFailed:(NSNumber *)requestId;
+ (void)reportRequestFailed:(NSNumber *)requestId cancelled:(BOOL)cancelled;
/**
* Store response body preview. This is an optional reporting method, and is a
@@ -109,9 +109,10 @@ static const NSMutableDictionary<NSNumber *, NSMutableString *> *responseBuffers
#endif
}
// TODO(T218584924): Implement and report to NetworkReporter
+ (void)reportRequestFailed:(NSNumber *)requestId
+ (void)reportRequestFailed:(NSNumber *)requestId cancelled:(bool)cancelled
{
NetworkReporter::getInstance().reportRequestFailed(requestId.stringValue.UTF8String, cancelled);
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: Clear buffer for request
if (responseBuffers != nullptr) {
@@ -437,8 +437,12 @@ RCT_EXPORT_MODULE()
[task start];
__weak RCTNetworkTask *weakTask = task;
NSNumber *requestId = [task.requestID copy];
return ^{
[weakTask cancel];
if (facebook::react::ReactNativeFeatureFlags::enableNetworkEventReporting()) {
[RCTInspectorNetworkReporter reportRequestFailed:requestId cancelled:YES];
}
if (cancellationBlock) {
cancellationBlock();
}
@@ -672,7 +676,7 @@ RCT_EXPORT_MODULE()
if (facebook::react::ReactNativeFeatureFlags::enableNetworkEventReporting()) {
if (error != nullptr) {
[RCTInspectorNetworkReporter reportRequestFailed:task.requestID];
[RCTInspectorNetworkReporter reportRequestFailed:task.requestID cancelled:NO];
} else {
[RCTInspectorNetworkReporter reportResponseEnd:task.requestID encodedDataLength:data.length];
}
@@ -107,6 +107,18 @@ folly::dynamic ResponseReceivedParams::toDynamic() const {
return params;
}
folly::dynamic LoadingFailedParams::toDynamic() const {
folly::dynamic params = folly::dynamic::object;
params["requestId"] = requestId;
params["timestamp"] = timestamp;
params["type"] = type;
params["errorText"] = errorText;
params["canceled"] = canceled;
return params;
}
folly::dynamic LoadingFinishedParams::toDynamic() const {
folly::dynamic params = folly::dynamic::object;
@@ -89,6 +89,19 @@ struct ResponseReceivedParams {
folly::dynamic toDynamic() const;
};
/**
* https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-loadingFailed
*/
struct LoadingFailedParams {
std::string requestId;
double timestamp;
std::string type;
std::string errorText;
bool canceled;
folly::dynamic toDynamic() const;
};
/**
* https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-loadingFinished
*/
@@ -151,19 +151,6 @@ void NetworkReporter::reportConnectionTiming(const std::string& requestId) {
#endif
}
void NetworkReporter::reportRequestFailed(
const std::string& /*requestId*/) const {
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: CDP event handling
if (!isDebuggingEnabledNoSync()) {
return;
}
// TODO(T218236855)
throw std::runtime_error("Not implemented");
#endif
}
void NetworkReporter::reportResponseStart(
const std::string& requestId,
const ResponseInfo& responseInfo,
@@ -190,11 +177,14 @@ void NetworkReporter::reportResponseStart(
auto response =
cdp::network::Response::fromInputParams(responseInfo, encodedDataLength);
auto resourceType = cdp::network::resourceTypeFromMimeType(response.mimeType);
resourceTypeMap_.emplace(requestId, resourceType);
auto params = cdp::network::ResponseReceivedParams{
.requestId = requestId,
.loaderId = "",
.timestamp = getCurrentUnixTimestampSeconds(),
.type = cdp::network::resourceTypeFromMimeType(response.mimeType),
.type = resourceType,
.response = response,
.hasExtraInfo = false,
};
@@ -273,6 +263,30 @@ void NetworkReporter::reportResponseEnd(
#endif
}
void NetworkReporter::reportRequestFailed(
const std::string& requestId,
bool cancelled) const {
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: CDP event handling
if (!isDebuggingEnabledNoSync()) {
return;
}
auto params = cdp::network::LoadingFailedParams{
.requestId = requestId,
.timestamp = getCurrentUnixTimestampSeconds(),
.type = resourceTypeMap_.find(requestId) != resourceTypeMap_.end()
? resourceTypeMap_.at(requestId)
: "Other",
.errorText = cancelled ? "net::ERR_ABORTED" : "net::ERR_FAILED",
.canceled = cancelled,
};
frontendChannel_(
cdp::jsonNotification("Network.loadingFailed", params.toDynamic()));
#endif
}
void NetworkReporter::storeResponseBody(
const std::string& requestId,
std::string_view body,
@@ -113,13 +113,6 @@ class NetworkReporter {
*/
void reportConnectionTiming(const std::string& requestId);
/**
* Report when a network request has failed.
*
* Corresponds to `Network.loadingFailed` in CDP.
*/
void reportRequestFailed(const std::string& requestId) const;
/**
* Report when HTTP response headers have been received, corresponding to
* when the first byte of the response is available.
@@ -152,6 +145,13 @@ class NetworkReporter {
*/
void reportResponseEnd(const std::string& requestId, int encodedDataLength);
/**
* Report when a network request has failed.
*
* Corresponds to `Network.loadingFailed` in CDP.
*/
void reportRequestFailed(const std::string& requestId, bool cancelled) const;
/**
* Store the fetched response body for a text or image network response.
* These may be retrieved by CDP clients to to render a response preview via
@@ -193,6 +193,9 @@ class NetworkReporter {
std::unordered_map<std::string, ResourceTimingData> perfTimingsBuffer_{};
std::mutex perfTimingsMutex_;
// Only populated when CDP debugging is enabled.
std::map<std::string, std::string> resourceTypeMap_{};
// Only populated when CDP debugging is enabled.
BoundedRequestBuffer requestBodyBuffer_{};
std::mutex requestBodyMutex_;