[oden] Start on a debugger interface compatible with chrome
This is trying to implement the chrome devtools protocol as documented here: https://chromedevtools.github.io/devtools-protocol/v8/ Doing it in rust and hoping to meet in the middle in quickjs, because I don't like how many decisions I need to make building a stand-alone quickjs debugger.
This commit is contained in:
parent
743a1cc623
commit
c990de5ad6
3 changed files with 142 additions and 0 deletions
|
|
@ -1376,6 +1376,8 @@ fn main_thread(event_loop: EventLoopProxy<OdenEvent>, state: State, reciever: Re
|
|||
let mut script = script::ScriptContext::new(None, script_reload_send.clone())
|
||||
.expect("Unable to create initial script context");
|
||||
|
||||
let _debugger = script::debugger::start_debugger();
|
||||
|
||||
const SPF: f64 = 1.0 / 60.0;
|
||||
loop {
|
||||
frame_mark();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use std::time::Instant;
|
|||
use tracy_client::span;
|
||||
use winit::event::*;
|
||||
|
||||
pub mod debugger;
|
||||
pub mod graphics;
|
||||
mod input;
|
||||
mod io;
|
||||
|
|
|
|||
139
src/script/debugger.rs
Normal file
139
src/script/debugger.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::thread;
|
||||
|
||||
pub struct Debugger {
|
||||
_thread: thread::JoinHandle<()>,
|
||||
}
|
||||
|
||||
fn write_http_response(
|
||||
stream: &mut TcpStream,
|
||||
code: u16,
|
||||
phrase: &str,
|
||||
content_type: &str,
|
||||
data: &[u8],
|
||||
) -> io::Result<()> {
|
||||
let length = data.len();
|
||||
let buffer = format!(
|
||||
"HTTP/1.1 {code} {phrase}\r\n\
|
||||
content-length: {length}\r\n\
|
||||
content-type: {content_type}\r\n\
|
||||
\r\n"
|
||||
);
|
||||
stream.write_all(buffer.as_bytes())?;
|
||||
stream.write_all(data)
|
||||
}
|
||||
|
||||
fn write_http_error(stream: &mut TcpStream, code: u16, phrase: &str) -> io::Result<()> {
|
||||
write_http_response(stream, code, phrase, "text/plain", phrase.as_bytes())
|
||||
}
|
||||
|
||||
fn write_http_ok(stream: &mut TcpStream, content_type: &str, data: &[u8]) -> io::Result<()> {
|
||||
write_http_response(stream, 200, "OK", content_type, data)
|
||||
}
|
||||
|
||||
fn handle_connection(mut stream: TcpStream) -> io::Result<()> {
|
||||
let mut buf_reader = BufReader::new(&mut stream);
|
||||
|
||||
let mut buffer = String::new();
|
||||
buf_reader.read_line(&mut buffer)?;
|
||||
|
||||
let parts: Vec<_> = buffer.trim().split(" ").collect();
|
||||
if parts.len() != 3 {
|
||||
eprintln!("Invalid request line: {buffer}");
|
||||
write_http_error(&mut stream, 400, "Invalid request")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let method = parts[0];
|
||||
let path = parts[1];
|
||||
eprintln!("Debugger: {method} {path}");
|
||||
|
||||
let mut headers = HashMap::new();
|
||||
let mut header_line_buffer = String::new();
|
||||
loop {
|
||||
header_line_buffer.clear();
|
||||
buf_reader.read_line(&mut header_line_buffer)?;
|
||||
let header_line = header_line_buffer.trim();
|
||||
if header_line.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let sep_idx = match header_line.find(":") {
|
||||
Some(idx) => idx,
|
||||
None => {
|
||||
write_http_error(&mut stream, 400, "Invalid request")?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let header = &header_line[0..sep_idx];
|
||||
let value = &header_line[sep_idx + 1..];
|
||||
eprintln!("HEADER: {header} {value}");
|
||||
headers.insert(header.trim().to_string(), value.trim().to_string());
|
||||
}
|
||||
|
||||
if method == "GET" {
|
||||
if path == "/json/version" {
|
||||
// NOTE: Deno spits out a V8 version here but we're not running v8.
|
||||
write_http_ok(
|
||||
&mut stream,
|
||||
"application/json",
|
||||
"{\"Browser\": \"Oden/0.0.1\", \"Protocol-Version\": \"1.3\"}".as_bytes(),
|
||||
)?;
|
||||
} else if path == "/json" || path == "/json/list" {
|
||||
// [
|
||||
// {
|
||||
// "description": "deno",
|
||||
// "devtoolsFrontendUrl": "devtools://devtools/bundled/js_app.html?ws=127.0.0.1:9229/ws/01b9bd23-8810-43ed-86f7-5feef5d120fa&experiments=true&v8only=true",
|
||||
// "faviconUrl": "https://deno.land/favicon.ico",
|
||||
// "id": "01b9bd23-8810-43ed-86f7-5feef5d120fa",
|
||||
// "title": "deno - main [pid: 40483]",
|
||||
// "type": "node",
|
||||
// "url": "file:///Users/doty/src/ioniq/status.ts",
|
||||
// "webSocketDebuggerUrl": "ws://127.0.0.1:9229/ws/01b9bd23-8810-43ed-86f7-5feef5d120fa"
|
||||
// }
|
||||
// ]
|
||||
} else if path == "/ws/dd5cfe85-f0b1-4241-a643-8ed81e436188" {
|
||||
// I don't feel like making a new guid for each thing.
|
||||
// Websocket upgrade and then debugger messages.
|
||||
} else {
|
||||
write_http_error(&mut stream, 404, "Not Found")?;
|
||||
}
|
||||
} else {
|
||||
write_http_error(&mut stream, 404, "Not Found")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn debugger_listener() -> io::Result<()> {
|
||||
let listener = TcpListener::bind("127.0.0.1:0")?;
|
||||
let port = listener.local_addr()?.port();
|
||||
println!("Debugger listening on http://127.0.0.1:{port}");
|
||||
for stream in listener.incoming() {
|
||||
let stream = match stream {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("Error accepting incoming connection: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Thread pool extraction here.
|
||||
if let Err(e) = handle_connection(stream) {
|
||||
eprintln!("Error handling incoming connection: {e}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn start_debugger() -> Debugger {
|
||||
let thread = thread::spawn(move || {
|
||||
if let Err(e) = debugger_listener() {
|
||||
eprintln!("ERROR STARTING DEBUGGER: {:?}", e);
|
||||
}
|
||||
});
|
||||
Debugger { _thread: thread }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue