Skip to content

Commit

Permalink
Implement Network.loadNetworkResource etc in C++ (#44845)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #44845

## Design
 - `NetworkIOAgent` is owned by the `HostAgent`.
 - `NetworkIOAgent` is passed any CDP requests not handled by the `HostAgent` itself, and before delegating to `InstanceAgent`.
 - It handles:
   - [`Network.loadNetworkResource`](https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-loadNetworkResource)
   - [`IO.read`](https://chromedevtools.github.io/devtools-protocol/tot/IO/#method-read)
   - [`IO.close`](https://chromedevtools.github.io/devtools-protocol/tot/IO/#method-close)
 - `NetworkIOAgent.loadNetworkResource` creates a `Stream` corresponding to a single resource download/upload. A reference is held in a map `streams_` until an error, the agent is disconnected (destroyed) or it is discarded by the frontend with `IO.close`.
 - `delegate.loadNetworkResource` is called with a `stream`-scoped executor, which it uses to call back with headers, data and errors.
 - Callbacks for `IO.read` requests are held by the `Stream` until the incoming data is complete or enough data is available to fill the request (an implementation choice to optimise for fewest round trips). Any incoming data or error causes any pending requests to be rechecked.

 {F1719616688}

## Unimplemented platforms
 - Platforms may optionally implement `HostTargetDelegate.networkRequest` (as of this diff, none do). If they don't we report a CDP "not implemented" error, similar to the status quo where it was unimplemented by the C++ agent.

Changelog:
[General][Added] Debugging: implement common C++ layer of CDP `Network.loadNetworkResource`

Reviewed By: motiz88

Differential Revision: D54309633

fbshipit-source-id: 51e416e9d537b253f72693952d5fd520b6ae11b6
  • Loading branch information
robhogan authored and facebook-github-bot committed Jul 12, 2024
1 parent 7d7d403 commit 193cdc3
Show file tree
Hide file tree
Showing 9 changed files with 1,319 additions and 17 deletions.
17 changes: 11 additions & 6 deletions packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ HostAgent::HostAgent(
FrontendChannel frontendChannel,
HostTargetController& targetController,
HostTargetMetadata hostMetadata,
SessionState& sessionState)
SessionState& sessionState,
VoidExecutor executor)
: frontendChannel_(frontendChannel),
targetController_(targetController),
hostMetadata_(std::move(hostMetadata)),
sessionState_(sessionState) {}
sessionState_(sessionState),
networkIOAgent_(NetworkIOAgent(frontendChannel, executor)) {}

void HostAgent::handleRequest(const cdp::PreparsedRequest& req) {
bool shouldSendOKResponse = false;
Expand Down Expand Up @@ -182,6 +184,12 @@ void HostAgent::handleRequest(const cdp::PreparsedRequest& req) {
frontendChannel_(cdp::jsonNotification(
"Tracing.tracingComplete",
folly::dynamic::object("dataLossOccurred", false)));
shouldSendOKResponse = true;
isFinishedHandlingRequest = true;
}

if (!isFinishedHandlingRequest &&
networkIOAgent_.handleRequest(req, targetController_.getDelegate())) {
return;
}

Expand All @@ -195,10 +203,7 @@ void HostAgent::handleRequest(const cdp::PreparsedRequest& req) {
return;
}

frontendChannel_(cdp::jsonError(
req.id,
cdp::ErrorCode::MethodNotFound,
req.method + " not implemented yet"));
throw NotImplementedException(req.method);
}

HostAgent::~HostAgent() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

#pragma once

#include "CdpJson.h"
#include "HostTarget.h"
#include "NetworkIOAgent.h"
#include "SessionState.h"

#include <jsinspector-modern/InspectorInterfaces.h>
Expand Down Expand Up @@ -39,12 +41,14 @@ class HostAgent final {
* HostTargetDelegate and underlying HostTarget both outlive the agent.
* \param hostMetadata Metadata about the host that created this agent.
* \param sessionState The state of the session that created this agent.
* \param exector A void executor to be used by async-aware handlers.
*/
HostAgent(
FrontendChannel frontendChannel,
HostTargetController& targetController,
HostTargetMetadata hostMetadata,
SessionState& sessionState);
SessionState& sessionState,
VoidExecutor executor);

HostAgent(const HostAgent&) = delete;
HostAgent(HostAgent&&) = delete;
Expand Down Expand Up @@ -104,6 +108,8 @@ class HostAgent final {
* during handleRequest and other method calls on the same thread.
*/
SessionState& sessionState_;

NetworkIOAgent networkIOAgent_;
};

} // namespace facebook::react::jsinspector_modern
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class HostTargetSession {
explicit HostTargetSession(
std::unique_ptr<IRemoteConnection> remote,
HostTargetController& targetController,
HostTargetMetadata hostMetadata)
HostTargetMetadata hostMetadata,
VoidExecutor executor)
: remote_(std::make_shared<RAIIRemoteConnection>(std::move(remote))),
frontendChannel_(
[remoteWeak = std::weak_ptr(remote_)](std::string_view message) {
Expand All @@ -41,7 +42,8 @@ class HostTargetSession {
frontendChannel_,
targetController,
std::move(hostMetadata),
state_) {}
state_,
executor) {}

/**
* Called by CallbackLocalConnection to send a message to this Session's
Expand All @@ -62,15 +64,22 @@ class HostTargetSession {
return;
}

// Catch exceptions that may arise from accessing dynamic params during
// request handling.
try {
hostAgent_.handleRequest(request);
} catch (const cdp::TypeError& e) {
}
// Catch exceptions that may arise from accessing dynamic params during
// request handling.
catch (const cdp::TypeError& e) {
frontendChannel_(
cdp::jsonError(request.id, cdp::ErrorCode::InvalidRequest, e.what()));
return;
}
// Catch exceptions for unrecognised or partially implemented CDP methods.
catch (const NotImplementedException& e) {
frontendChannel_(
cdp::jsonError(request.id, cdp::ErrorCode::MethodNotFound, e.what()));
return;
}
}

/**
Expand Down Expand Up @@ -148,7 +157,10 @@ HostTarget::HostTarget(HostTargetDelegate& delegate)
std::unique_ptr<ILocalConnection> HostTarget::connect(
std::unique_ptr<IRemoteConnection> connectionToFrontend) {
auto session = std::make_shared<HostTargetSession>(
std::move(connectionToFrontend), controller_, delegate_.getMetadata());
std::move(connectionToFrontend),
controller_,
delegate_.getMetadata(),
makeVoidExecutor(executorFromThis()));
session->setCurrentInstance(currentInstance_.get());
sessions_.insert(std::weak_ptr(session));
return std::make_unique<CallbackLocalConnection>(
Expand Down
22 changes: 18 additions & 4 deletions packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "HostCommand.h"
#include "InspectorInterfaces.h"
#include "InstanceTarget.h"
#include "NetworkIOAgent.h"
#include "ScopedExecutor.h"
#include "WeakList.h"

Expand Down Expand Up @@ -50,13 +51,13 @@ struct HostTargetMetadata {
* React Native platform needs to implement in order to integrate with the
* debugging stack.
*/
class HostTargetDelegate {
class HostTargetDelegate : public LoadNetworkResourceDelegate {
public:
HostTargetDelegate() = default;
HostTargetDelegate(const HostTargetDelegate&) = delete;
HostTargetDelegate(HostTargetDelegate&&) = default;
HostTargetDelegate(HostTargetDelegate&&) = delete;
HostTargetDelegate& operator=(const HostTargetDelegate&) = delete;
HostTargetDelegate& operator=(HostTargetDelegate&&) = default;
HostTargetDelegate& operator=(HostTargetDelegate&&) = delete;

// TODO(moti): This is 1:1 the shape of the corresponding CDP message -
// consider reusing typed/generated CDP interfaces when we have those.
Expand Down Expand Up @@ -92,7 +93,7 @@ class HostTargetDelegate {
}
};

virtual ~HostTargetDelegate();
virtual ~HostTargetDelegate() override;

/**
* Returns a metadata object describing the host. This is called on an
Expand All @@ -119,6 +120,19 @@ class HostTargetDelegate {
*/
virtual void onSetPausedInDebuggerMessage(
const OverlaySetPausedInDebuggerMessageRequest& request) = 0;

/**
* Called by NetworkIOAgent on handling a `Network.loadNetworkResource` CDP
* request. Platform implementations should override this to perform a
* network request of the given URL, and use listener's callbacks on receipt
* of headers, data chunks, and errors.
*/
void loadNetworkResource(
const LoadNetworkResourceRequest& /*params*/,
ScopedExecutor<NetworkRequestListener> /*executor*/) override {
throw NotImplementedException(
"LoadNetworkResourceDelegate.loadNetworkResource is not implemented by this host target delegate.");
}
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,19 @@ class JSINSPECTOR_EXPORT IInspector : public IDestructible {
std::weak_ptr<IPageStatusListener> listener) = 0;
};

class NotImplementedException : public std::exception {
public:
explicit NotImplementedException(std::string message)
: msg_(std::move(message)) {}

const char* what() const noexcept override {
return msg_.c_str();
}

private:
std::string msg_;
};

/// getInspectorInstance retrieves the singleton inspector that tracks all
/// debuggable pages in this process.
extern IInspector& getInspectorInstance();
Expand Down
Loading

0 comments on commit 193cdc3

Please sign in to comment.