Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help connecting to a Node.js process with --inspect #257

Open
arimus opened this issue Feb 28, 2025 · 0 comments
Open

Help connecting to a Node.js process with --inspect #257

arimus opened this issue Feb 28, 2025 · 0 comments

Comments

@arimus
Copy link

arimus commented Feb 28, 2025

I'm trying to connect to a node.js process which is setup with --inspect and listening on 9229. I'm able to connect, enable runtime, etc. However, what I can't do is get any of the events for the inspect to come through chromiumoxide. They are being sent over the wire just fine and I can parse them using a raw websocket client (see data below). However, chromiumoxide seems to be filtering them and never processing them as part of the even listener streams I've set up.

Any suggestions / assistance?

Test code:

use chromiumoxide::browser::Browser;
use chromiumoxide::cdp::js_protocol::runtime::{EnableParams, EvaluateParams, EventConsoleApiCalled};
use futures::StreamExt;
use reqwest;
use serde::Deserialize;
use std::error::Error;
use std::time::Duration;
use tokio::time::sleep;

#[derive(Debug, Deserialize)]
struct TargetInfo {
    #[serde(rename = "webSocketDebuggerUrl")]
    web_socket_debugger_url: Option<String>,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // Poll the /json/list endpoint until a target with a valid WebSocket URL is found.
    let json_endpoint = "http://localhost:9229/json/list";
    let ws_url = loop {
        match reqwest::get(json_endpoint).await {
            Ok(resp) if resp.status().is_success() => {
                match resp.json::<Vec<TargetInfo>>().await {
                    Ok(targets) => {
                        if let Some(target) = targets.iter().find(|t| t.web_socket_debugger_url.is_some()) {
                            let url = target.web_socket_debugger_url.as_ref().unwrap().to_string();
                            println!("Found WebSocket URL: {}", url);
                            break url;
                        } else {
                            eprintln!("No target with a valid WebSocket URL found. Retrying in 1 second...");
                        }
                    }
                    Err(e) => {
                        eprintln!("Failed to parse JSON: {}. Retrying in 1 second...", e);
                    }
                }
            }
            Ok(resp) => {
                eprintln!("Received non-success status {}. Retrying in 1 second...", resp.status());
            }
            Err(e) => {
                eprintln!("Failed to get target info: {}. Retrying in 1 second...", e);
            }
        }
        sleep(Duration::from_secs(1)).await;
    };

    // Connect to the target using the obtained WebSocket URL.
    let (browser, mut handler) = Browser::connect(&ws_url).await?;
    println!("Connected to {}", ws_url);

    // Spawn a background task that prints all raw CDP events from the connection.
    tokio::spawn(async move {
        while let Some(raw_event) = handler.next().await {
            println!("Raw CDP event: {:?}", raw_event);
        }
    });

    // Optionally, enable the Runtime domain if you want to evaluate scripts.
    browser.execute(EnableParams::default()).await?;

    // Subscribe to console API events.
    let mut console_events = browser.event_listener::<EventConsoleApiCalled>().await?;
    println!("Subscribed to Runtime.consoleAPICalled events.");

    // Wait briefly to ensure the event subscription is fully active.
    sleep(Duration::from_millis(500)).await;

    // Optionally, evaluate a script that triggers a console.log.
    let eval_script = r#"
      setTimeout(() => {
          console.log('Test message from evaluated script');
      }, 100);
    "#;
    let _ = browser.execute(EvaluateParams::new(eval_script)).await?;
    println!("Evaluate command executed.");

    // Listen for both types of events.
    loop {
        tokio::select! {
            Some(exec_event) = console_events.next() => {
                println!("Console event: {:?}", exec_event);
            },
            _ = sleep(Duration::from_secs(1)) => {
                // No events, loop and wait.
            },
        }
    }
}

Over the wire:

{
  "method": "Runtime.consoleAPICalled",
  "params": {
    "type": "debug",
    "args": [
      {
        "type": "string",
        "value": "[2025-02-28 13:16:15.534] \u001b[90mdefault\u001b[39m \u001b[37mdebug\u001b[39m"
      },
      {
        "type": "string",
        "value": "InjectLogger() injecting logger for target"
      },
      {
        "type": "string",
        "value": "DatabaseStorageEngineManager"
      }
    ],
    "executionContextId": 1,
    "timestamp": 1740773775537.119,
    "stackTrace": {
      "callFrames": [
        {
          "functionName": "log",
          "scriptId": "309",
          "url": "file:///Users/arimus/workspace/test/packages/logging/dist/adapters/console/ConsoleLoggerAdapter.js",
          "lineNumber": 133,
          "columnNumber": 41
        }
      ]
    }
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant