267 lines
8.3 KiB
C
267 lines
8.3 KiB
C
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "CdpJson.h"
|
||
|
#include "InspectorInterfaces.h"
|
||
|
#include "ScopedExecutor.h"
|
||
|
|
||
|
#include <folly/dynamic.h>
|
||
|
#include <mutex>
|
||
|
#include <sstream>
|
||
|
#include <string>
|
||
|
#include <unordered_map>
|
||
|
#include <utility>
|
||
|
#include <variant>
|
||
|
|
||
|
namespace facebook::react::jsinspector_modern {
|
||
|
|
||
|
using StreamID = const std::string;
|
||
|
using Headers = std::map<std::string, std::string>;
|
||
|
using IOReadError = const std::string;
|
||
|
|
||
|
namespace {
|
||
|
class Stream; // Defined in NetworkIOAgent.cpp
|
||
|
using StreamsMap = std::unordered_map<std::string, std::shared_ptr<Stream>>;
|
||
|
} // namespace
|
||
|
|
||
|
struct LoadNetworkResourceRequest {
|
||
|
std::string url;
|
||
|
};
|
||
|
|
||
|
struct ReadStreamParams {
|
||
|
StreamID handle;
|
||
|
std::optional<unsigned long> size;
|
||
|
std::optional<unsigned long> offset;
|
||
|
};
|
||
|
|
||
|
struct NetworkResource {
|
||
|
bool success{};
|
||
|
std::optional<std::string> stream;
|
||
|
std::optional<int> httpStatusCode;
|
||
|
std::optional<std::string> netErrorName;
|
||
|
std::optional<Headers> headers;
|
||
|
folly::dynamic toDynamic() const {
|
||
|
auto dynamicResource = folly::dynamic::object("success", success);
|
||
|
|
||
|
if (success) { // stream IFF successful
|
||
|
assert(stream);
|
||
|
dynamicResource("stream", *stream);
|
||
|
}
|
||
|
|
||
|
if (netErrorName) { // Only if unsuccessful
|
||
|
assert(!success);
|
||
|
dynamicResource("netErrorName", *netErrorName);
|
||
|
}
|
||
|
|
||
|
if (httpStatusCode) { // Guaranteed if successful
|
||
|
dynamicResource("httpStatusCode", *httpStatusCode);
|
||
|
} else {
|
||
|
assert(!success);
|
||
|
}
|
||
|
|
||
|
if (headers) { // Guaranteed if successful
|
||
|
auto dynamicHeaders = folly::dynamic::object();
|
||
|
for (const auto& pair : *headers) {
|
||
|
dynamicHeaders(pair.first, pair.second);
|
||
|
}
|
||
|
dynamicResource("headers", std::move(dynamicHeaders));
|
||
|
} else {
|
||
|
assert(!success);
|
||
|
}
|
||
|
return dynamicResource;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct IOReadResult {
|
||
|
std::string data;
|
||
|
bool eof;
|
||
|
bool base64Encoded;
|
||
|
folly::dynamic toDynamic() const {
|
||
|
auto obj = folly::dynamic::object("data", data);
|
||
|
obj("eof", eof);
|
||
|
obj("base64Encoded", base64Encoded);
|
||
|
return obj;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Passed to `loadNetworkResource`, provides callbacks for processing incoming
|
||
|
* data and other events.
|
||
|
*/
|
||
|
class NetworkRequestListener {
|
||
|
public:
|
||
|
NetworkRequestListener() = default;
|
||
|
NetworkRequestListener(const NetworkRequestListener&) = delete;
|
||
|
NetworkRequestListener& operator=(const NetworkRequestListener&) = delete;
|
||
|
NetworkRequestListener(NetworkRequestListener&&) noexcept = default;
|
||
|
NetworkRequestListener& operator=(NetworkRequestListener&&) noexcept =
|
||
|
default;
|
||
|
virtual ~NetworkRequestListener() = default;
|
||
|
|
||
|
/**
|
||
|
* To be called by the delegate on receipt of response headers, including
|
||
|
* on "unsuccessful" status codes.
|
||
|
*
|
||
|
* \param httpStatusCode The HTTP status code received.
|
||
|
* \param headers Response headers as an unordered_map.
|
||
|
*/
|
||
|
virtual void onHeaders(int httpStatusCode, const Headers& headers) = 0;
|
||
|
|
||
|
/**
|
||
|
* To be called by the delegate on receipt of data chunks.
|
||
|
* \param data The data received.
|
||
|
*/
|
||
|
virtual void onData(std::string_view data) = 0;
|
||
|
|
||
|
/**
|
||
|
* To be called by the delegate on any error with the request, either before
|
||
|
* headers are received or for a subsequent interrupion.
|
||
|
*
|
||
|
* \param message A short, human-readable message, which may be forwarded to
|
||
|
* the CDP client either in the `loadNetworkResource` response (if headers
|
||
|
* were not yet received), or as a CDP error in response to a subsequent
|
||
|
* `IO.read`.
|
||
|
*/
|
||
|
virtual void onError(const std::string& message) = 0;
|
||
|
|
||
|
/**
|
||
|
* To be called by the delegate on successful completion of the request.
|
||
|
* Delegates must call *either* onCompletion() or onError() exactly once.
|
||
|
*/
|
||
|
virtual void onCompletion() = 0;
|
||
|
|
||
|
/**
|
||
|
* Optionally (preferably) used to give NetworkIOAgent
|
||
|
a way to cancel an
|
||
|
* in-progress download.
|
||
|
*
|
||
|
* \param cancelFunction A function that can be called to cancel a download,
|
||
|
* may be called before or after the download is complete.
|
||
|
*/
|
||
|
virtual void setCancelFunction(std::function<void()> cancelFunction) = 0;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Implemented by the HostTargetDelegate per-platform to perform network
|
||
|
* requests.
|
||
|
*/
|
||
|
class LoadNetworkResourceDelegate {
|
||
|
public:
|
||
|
LoadNetworkResourceDelegate() = default;
|
||
|
LoadNetworkResourceDelegate(const LoadNetworkResourceDelegate&) = delete;
|
||
|
LoadNetworkResourceDelegate& operator=(const LoadNetworkResourceDelegate&) =
|
||
|
delete;
|
||
|
LoadNetworkResourceDelegate(LoadNetworkResourceDelegate&&) noexcept = delete;
|
||
|
LoadNetworkResourceDelegate& operator=(
|
||
|
LoadNetworkResourceDelegate&&) noexcept = delete;
|
||
|
virtual ~LoadNetworkResourceDelegate() = default;
|
||
|
|
||
|
/**
|
||
|
* 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 (via the executor) on receipt of headers, data chunks,
|
||
|
* and errors.
|
||
|
*
|
||
|
* \param params A LoadNetworkResourceRequest, including the url.
|
||
|
* \param executor A listener-scoped executor used by the delegate to execute
|
||
|
* listener callbacks on headers, data chunks, and errors. Implementations
|
||
|
* *should* call listener->setCancelFunction() to provide a lambda that can be
|
||
|
* called to abort any in-flight network operation that is no longer needed.
|
||
|
*/
|
||
|
virtual void loadNetworkResource(
|
||
|
[[maybe_unused]] const LoadNetworkResourceRequest& params,
|
||
|
[[maybe_unused]] ScopedExecutor<NetworkRequestListener> executor) = 0;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Provides an agent for handling CDP's Network.loadNetworkResource, IO.read and
|
||
|
* IO.close.
|
||
|
*
|
||
|
* Owns state of all in-progress and completed HTTP requests - ensure
|
||
|
* IO.close is used to free resources once consumed.
|
||
|
*
|
||
|
* Public methods must be called the same thread as the given executor.
|
||
|
*/
|
||
|
class NetworkIOAgent {
|
||
|
public:
|
||
|
/**
|
||
|
* \param frontendChannel A channel used to send responses to the
|
||
|
* frontend.
|
||
|
* \param executor An executor used for any callbacks provided, and for
|
||
|
* processing incoming data or other events from network operations.
|
||
|
*/
|
||
|
NetworkIOAgent(FrontendChannel frontendChannel, VoidExecutor executor)
|
||
|
: frontendChannel_(frontendChannel),
|
||
|
executor_(executor),
|
||
|
streams_(std::make_shared<StreamsMap>()) {}
|
||
|
|
||
|
/**
|
||
|
* Handle a CDP request. The response will be sent over the provided
|
||
|
* \c FrontendChannel synchronously or asynchronously.
|
||
|
* \param req The parsed request.
|
||
|
*/
|
||
|
bool handleRequest(
|
||
|
const cdp::PreparsedRequest& req,
|
||
|
LoadNetworkResourceDelegate& delegate);
|
||
|
|
||
|
private:
|
||
|
/**
|
||
|
* A channel used to send responses and events to the frontend.
|
||
|
*/
|
||
|
FrontendChannel frontendChannel_;
|
||
|
|
||
|
/**
|
||
|
* An executor used to create NetworkRequestListener-scoped executors for the
|
||
|
* delegate.
|
||
|
*/
|
||
|
VoidExecutor executor_;
|
||
|
|
||
|
/**
|
||
|
* Map of stream objects, which contain data received, accept read requests
|
||
|
* and listen for delegate events. Delegates have a scoped executor for Stream
|
||
|
* instances, but Streams will not live beyond the destruction of this
|
||
|
* NetworkIOAgent instance + executor scope.
|
||
|
*
|
||
|
* This is a shared_ptr so that we may capture a weak_ptr in our
|
||
|
* Stream::create callback without creating a cycle.
|
||
|
*/
|
||
|
std::shared_ptr<StreamsMap> streams_;
|
||
|
|
||
|
/**
|
||
|
* Stream IDs are strings of an incrementing integer, unique within each
|
||
|
* NewtworkIOAgent instance. This stores the next one to use.
|
||
|
*/
|
||
|
unsigned long nextStreamId_{0};
|
||
|
|
||
|
/**
|
||
|
* Begin loading an HTTP resource, delegating platform-specific
|
||
|
* implementation, responding to the frontend on headers received or on error.
|
||
|
* Does not catch exceptions thrown by the delegate (such as
|
||
|
* NotImplementedException).
|
||
|
*/
|
||
|
void handleLoadNetworkResource(
|
||
|
const cdp::PreparsedRequest& req,
|
||
|
LoadNetworkResourceDelegate& delegate);
|
||
|
|
||
|
/**
|
||
|
* Handle an IO.read CDP request. Emit a chunk of data from the stream, once
|
||
|
* enough has been downloaded, or report an error.
|
||
|
*/
|
||
|
void handleIoRead(const cdp::PreparsedRequest& req);
|
||
|
|
||
|
/**
|
||
|
* Handle an IO.close CDP request. Safely aborts any in-flight request.
|
||
|
* Reports CDP ok if the stream is found, or a CDP error if not.
|
||
|
*/
|
||
|
void handleIoClose(const cdp::PreparsedRequest& req);
|
||
|
};
|
||
|
|
||
|
} // namespace facebook::react::jsinspector_modern
|