Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion include/InspectorHTTPServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class InspectorHTTPServer {

void sendConsoleMessage(JSContextRef context, const char* level, const char* text);

// Streams a single network metric to the connected frontend (no server-side storage).
void sendNetworkMetric(JSContextRef context, const char* paramsJson);

void registerScript(const char* url, const char* source);

// Called when frontend sends Page.reload.
Expand All @@ -69,6 +72,8 @@ class InspectorHTTPServer {
std::string generateTargetListJSON();

void handleCDPMessage(SoupWebsocketConnection* connection, const char* message);
bool sendText(SoupWebsocketConnection* connection, const std::string& message);
SoupWebsocketConnection* findConnectionForContext(JSGlobalContextRef context);

struct ContextInfo {
JSGlobalContextRef context;
Expand All @@ -87,6 +92,8 @@ class InspectorHTTPServer {
int m_port;
std::map<JSGlobalContextRef, ContextInfo> m_contexts;
std::map<SoupWebsocketConnection*, JSGlobalContextRef> m_connections;
std::mutex m_connectionsMutex;
std::mutex m_sendMutex;
std::map<std::string, ScriptInfo> m_scripts;
std::mutex m_scriptsMutex;
uint64_t m_nextContextId;
Expand All @@ -95,4 +102,3 @@ class InspectorHTTPServer {
};

#endif // REMOTE_INSPECTOR_ENABLE

3 changes: 3 additions & 0 deletions include/jsc/JavaScriptContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class JavaScriptContext: public JavaScriptContextBase, public NetworkMetricsList
virtual void onMetricsData (NetworkMetrics *net) override;
rtMapObject* getNetworkMetricsData() const { return mNetworkMetricsData; }
void dumpNetworkMetricData(NetworkMetrics *metrics, std::string appUrl);
void setNetworkCaptureEnabled(bool enabled);

void setCreateApplicationStartTime(double time);
void setCreateApplicationEndTime(double time,uint32_t id);
Expand Down Expand Up @@ -116,6 +117,8 @@ class JavaScriptContext: public JavaScriptContextBase, public NetworkMetricsList
JSGlobalContextRef mContext;
PerformanceMetrics mPerformanceMetrics;
rtMapObject* mNetworkMetricsData;
std::mutex mNetworkMetricsMutex;
Comment thread
gurpreet319 marked this conversation as resolved.
bool mNetworkCaptureEnabled = false;
rtRef<rtJSCContextPrivate> mPriv;
rtRef<rtFunctionCallback> m_webSocketBinding;
rtRef<rtFunctionCallback> m_webSocketServerBinding;
Expand Down
114 changes: 76 additions & 38 deletions src/InspectorHTTPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,32 @@ InspectorHTTPServer::~InspectorHTTPServer()
stop();
}

bool InspectorHTTPServer::sendText(SoupWebsocketConnection* connection, const std::string& message)
{
if (!connection) {
return false;
}

std::lock_guard<std::mutex> sendLock(m_sendMutex);
if (soup_websocket_connection_get_state(connection) != SOUP_WEBSOCKET_STATE_OPEN) {
return false;
}

soup_websocket_connection_send_text(connection, message.c_str());
return true;
}

SoupWebsocketConnection* InspectorHTTPServer::findConnectionForContext(JSGlobalContextRef context)
{
std::lock_guard<std::mutex> connectionsLock(m_connectionsMutex);
for (const auto& pair : m_connections) {
if (pair.second == context) {
Comment on lines +109 to +113
return pair.first;
}
}
return nullptr;
}

bool InspectorHTTPServer::start(const char* address, int port)
{
if (m_server) {
Expand Down Expand Up @@ -154,11 +180,12 @@ void InspectorHTTPServer::stop()
m_server = nullptr;
NativeJSLogger::log(INFO, "InspectorHTTPServer: Stopped\n");
}


std::lock_guard<std::mutex> connectionsLock(m_connectionsMutex);
for (auto& pair : m_connections) {
g_object_unref(pair.first);
}

m_contexts.clear();
m_connections.clear();
}
Expand Down Expand Up @@ -200,25 +227,13 @@ void InspectorHTTPServer::sendConsoleMessage(JSContextRef context, const char* l

JSGlobalContextRef globalContext = const_cast<JSGlobalContextRef>(context);

SoupWebsocketConnection* targetConnection = nullptr;
for (const auto& pair : m_connections) {
if (pair.second == globalContext) {
targetConnection = pair.first;
break;
}
}

SoupWebsocketConnection* targetConnection = findConnectionForContext(globalContext);

if (!targetConnection) {
NativeJSLogger::log(DEBUG, "InspectorHTTPServer: No WebSocket connection found for this context\n");
return;
}

SoupWebsocketState state = soup_websocket_connection_get_state(targetConnection);
if (state != SOUP_WEBSOCKET_STATE_OPEN) {
NativeJSLogger::log(DEBUG, "InspectorHTTPServer: WebSocket connection is not open (state=%d)\n", state);
return;
}


const std::string escapedText = escapeJSON(text);

std::ostringstream event;
Expand All @@ -230,8 +245,25 @@ void InspectorHTTPServer::sendConsoleMessage(JSContextRef context, const char* l
<< "}}";

std::string eventStr = event.str();
soup_websocket_connection_send_text(targetConnection, eventStr.c_str());

if (!sendText(targetConnection, eventStr)) {
NativeJSLogger::log(DEBUG, "InspectorHTTPServer: WebSocket connection is not open for console event\n");
}
}

void InspectorHTTPServer::sendNetworkMetric(JSContextRef context, const char* paramsJson)
{
if (!context || !paramsJson) return;

JSGlobalContextRef globalContext = const_cast<JSGlobalContextRef>(context);

SoupWebsocketConnection* targetConnection = findConnectionForContext(globalContext);

if (!targetConnection) return;

std::ostringstream event;
event << "{\"method\":\"JSRuntime.networkMetric\",\"params\":" << paramsJson << "}";
std::string eventStr = event.str();
sendText(targetConnection, eventStr);
}

void InspectorHTTPServer::setReloadCallback(std::function<void()> callback)
Expand Down Expand Up @@ -270,11 +302,9 @@ void InspectorHTTPServer::registerScript(const char* url, const char* source)
<< "}}";
std::string evtStr = evt.str();

std::lock_guard<std::mutex> connectionsLock(m_connectionsMutex);
for (const auto& pair : m_connections) {
SoupWebsocketConnection* conn = pair.first;
if (soup_websocket_connection_get_state(conn) == SOUP_WEBSOCKET_STATE_OPEN) {
soup_websocket_connection_send_text(conn, evtStr.c_str());
}
sendText(pair.first, evtStr);
}
}

Expand Down Expand Up @@ -416,7 +446,10 @@ void InspectorHTTPServer::onWebSocketRequest(SoupServer* server, SoupServerMessa

if (targetContext) {
g_object_ref(connection);
self->m_connections[connection] = targetContext;
{
std::lock_guard<std::mutex> connectionsLock(self->m_connectionsMutex);
self->m_connections[connection] = targetContext;
}

g_signal_connect(connection, "message", G_CALLBACK(onWebSocketMessage), userData);
g_signal_connect(connection, "closed", G_CALLBACK(onWebSocketClosed), userData);
Expand Down Expand Up @@ -455,7 +488,8 @@ void InspectorHTTPServer::onWebSocketMessage(SoupWebsocketConnection* connection
void InspectorHTTPServer::onWebSocketClosed(SoupWebsocketConnection* connection, gpointer userData)
{
InspectorHTTPServer* self = static_cast<InspectorHTTPServer*>(userData);


std::lock_guard<std::mutex> connectionsLock(self->m_connectionsMutex);
auto it = self->m_connections.find(connection);
if (it != self->m_connections.end()) {
NativeJSLogger::log(DEBUG, "InspectorHTTPServer: WebSocket connection closed\n");
Expand All @@ -466,14 +500,17 @@ void InspectorHTTPServer::onWebSocketClosed(SoupWebsocketConnection* connection,

void InspectorHTTPServer::handleCDPMessage(SoupWebsocketConnection* connection, const char* message)
{
auto it = m_connections.find(connection);
if (it == m_connections.end()) {
NativeJSLogger::log(DEBUG, "InspectorHTTPServer: No context for connection\n");
return;
JSGlobalContextRef context = nullptr;
{
std::lock_guard<std::mutex> connectionsLock(m_connectionsMutex);
auto it = m_connections.find(connection);
if (it == m_connections.end()) {
NativeJSLogger::log(DEBUG, "InspectorHTTPServer: No context for connection\n");
return;
}
context = it->second;
}

JSGlobalContextRef context = it->second;


std::string msg(message);

size_t idPos = msg.find("\"id\":");
Expand Down Expand Up @@ -505,7 +542,7 @@ void InspectorHTTPServer::handleCDPMessage(SoupWebsocketConnection* connection,
<< "\"name\":\"RDK NativeScript\""
<< "}}}";
std::string eventStr = contextCreated.str();
soup_websocket_connection_send_text(connection, eventStr.c_str());
sendText(connection, eventStr);
}
else if (method == "Debugger.enable" ||
method == "Console.enable" || method == "Runtime.runIfWaitingForDebugger" ||
Expand All @@ -531,7 +568,7 @@ void InspectorHTTPServer::handleCDPMessage(SoupWebsocketConnection* connection,
<< "\"executionContextId\":1,\"hash\":\"\""
<< "}}";
std::string evtStr = scriptParsed.str();
soup_websocket_connection_send_text(connection, evtStr.c_str());
sendText(connection, evtStr);
}
}
}
Expand Down Expand Up @@ -624,12 +661,12 @@ void InspectorHTTPServer::handleCDPMessage(SoupWebsocketConnection* connection,
// Ack first so frontend does not time out.
response << "{\"id\":" << messageId << ",\"result\":{}}";
std::string ackStr = response.str();
soup_websocket_connection_send_text(connection, ackStr.c_str());
sendText(connection, ackStr);

const char* destroyedEvt =
"{\"method\":\"Runtime.executionContextDestroyed\","
"\"params\":{\"executionContextId\":1}}";
soup_websocket_connection_send_text(connection, destroyedEvt);
sendText(connection, destroyedEvt);

{
std::lock_guard<std::mutex> lock(m_scriptsMutex);
Expand All @@ -647,7 +684,7 @@ void InspectorHTTPServer::handleCDPMessage(SoupWebsocketConnection* connection,
const char* createdEvt =
"{\"method\":\"Runtime.executionContextCreated\",\"params\":{\"context\":{"
"\"id\":1,\"origin\":\"\",\"name\":\"RDK NativeScript\"}}}";
soup_websocket_connection_send_text(connection, createdEvt);
sendText(connection, createdEvt);

NativeJSLogger::log(INFO, "InspectorHTTPServer: Page.reload complete\n");
return;
Expand Down Expand Up @@ -734,8 +771,9 @@ void InspectorHTTPServer::handleCDPMessage(SoupWebsocketConnection* connection,
}

std::string responseStr = response.str();
soup_websocket_connection_send_text(connection, responseStr.c_str());
sendText(connection, responseStr);

}

#endif // REMOTE_INSPECTOR_ENABLE

90 changes: 87 additions & 3 deletions src/jsc/JavaScriptContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -591,16 +591,94 @@
}
}


static std::string jsonEscape(const char* str)
{
std::string out;
if (!str) return out;
static const char* hex = "0123456789abcdef";
for (const unsigned char* p = reinterpret_cast<const unsigned char*>(str); *p; ++p) {
switch (*p) {

Check failure on line 601 in src/jsc/JavaScriptContext.cpp

View workflow job for this annotation

GitHub Actions / call-fossid-workflow / Fossid Annotate PR

FossID Detected License Issue

Snippet with 'GPL-3.0-or-later, Commercial' file licenses found (10 lines) Location: https://github.com/21cncstudio/project_aura/blob/v1.1.0/src/modules/MqttPayloadBuilder.cpp#L25 Component: pkg:github/21cncstudio/project_aura@v1.1.0 Download: https://github.com/21cncstudio/project_aura/archive/refs/tags/v1.1.0.tar.gz
case '"': out += "\\\""; break;
case '\\': out += "\\\\"; break;
case '\b': out += "\\b"; break;
case '\f': out += "\\f"; break;
case '\n': out += "\\n"; break;
case '\r': out += "\\r"; break;
case '\t': out += "\\t"; break;
default:
if (*p < 0x20) {
out += "\\u00";
out += hex[(*p >> 4) & 0x0f];
out += hex[*p & 0x0f];
} else {
out += static_cast<char>(*p);
}
}
}
return out;
}

static std::string buildNetworkMetricPayload(const NetworkMetrics& net)
{
std::ostringstream params;
params << "{\"url\":\"" << jsonEscape(net.url.cString()) << "\","
<< "\"method\":\"" << jsonEscape(net.method.cString()) << "\","
<< "\"statusCode\":" << net.statusCode << ","
<< "\"headers\":[";

for (size_t j = 0; j < net.headers.size(); ++j) {
if (j) {
params << ",";
}
params << "\"" << jsonEscape(net.headers[j].cString()) << "\"";
}

params << "],\"timeMetricsData\":[";
bool firstMetric = true;
for (const auto& metric : net.timeMetricsData) {
if (!firstMetric) {
params << ",";
}
firstMetric = false;

const rtString metricKey = metric.first;
const rtString metricValue = metric.second.toString();
params << "{\"" << jsonEscape(metricKey.cString()) << "\":\""
<< jsonEscape(metricValue.cString()) << "\"}";
}

params << "]}";
return params.str();
}

void JavaScriptContext::onMetricsData (NetworkMetrics *net)
{
if (!net) {
if (!net) {
rtLogError("onMetricsData: Received null NetworkMetrics structure.");
return;
}
rtString key = net->url;
mNetworkMetricsData->set(key, rtValue((void *)net));

bool networkCaptureEnabled = false;
{
std::lock_guard<std::mutex> lock(mNetworkMetricsMutex);
networkCaptureEnabled = mNetworkCaptureEnabled;
}

if (!networkCaptureEnabled) {
delete net;
return;
}

const std::string params = buildNetworkMetricPayload(*net);

#ifdef REMOTE_INSPECTOR_ENABLE
InspectorHTTPServer::singleton().sendNetworkMetric(mContext, params.c_str());
#endif
Comment on lines +675 to +677

dumpNetworkMetricData(net, this->getUrl());
delete net;
net = nullptr;
}

void JavaScriptContext::dumpNetworkMetricData(NetworkMetrics *metrics, std::string appUrl)
Expand Down Expand Up @@ -688,3 +766,9 @@
mExternalApplicationHandler->runExternalApplication(url, mIds);
}
}

void JavaScriptContext::setNetworkCaptureEnabled(bool enabled)
{
std::lock_guard<std::mutex> lock(mNetworkMetricsMutex);
mNetworkCaptureEnabled = enabled;
}
Loading
Loading