diff --git a/cli/tests/integration/inspector_tests.rs b/cli/tests/integration/inspector_tests.rs index 6b2deb0bff..79422ee5a8 100644 --- a/cli/tests/integration/inspector_tests.rs +++ b/cli/tests/integration/inspector_tests.rs @@ -9,6 +9,8 @@ use deno_runtime::deno_fetch::reqwest; use fastwebsockets::FragmentCollector; use fastwebsockets::Frame; use fastwebsockets::WebSocket; +use http::header::HOST; +use hyper::header::HeaderValue; use hyper::upgrade::Upgraded; use hyper::Body; use hyper::Request; @@ -704,14 +706,34 @@ async fn inspector_json() { let mut url = ws_url.clone(); let _ = url.set_scheme("http"); url.set_path("/json"); - let resp = reqwest::get(url).await.unwrap(); - assert_eq!(resp.status(), reqwest::StatusCode::OK); - let endpoint_list: Vec = - serde_json::from_str(&resp.text().await.unwrap()).unwrap(); - let matching_endpoint = endpoint_list - .iter() - .find(|e| e["webSocketDebuggerUrl"] == ws_url.as_str()); - assert!(matching_endpoint.is_some()); + let client = reqwest::Client::new(); + + // Ensure that the webSocketDebuggerUrl matches the host header + for (host, expected) in [ + (None, ws_url.as_str()), + (Some("some.random.host"), "ws://some.random.host/"), + (Some("some.random.host:1234"), "ws://some.random.host:1234/"), + (Some("[::1]:1234"), "ws://[::1]:1234/"), + ] { + let mut req = reqwest::Request::new(reqwest::Method::GET, url.clone()); + if let Some(host) = host { + req + .headers_mut() + .insert(HOST, HeaderValue::from_static(host)); + } + let resp = client.execute(req).await.unwrap(); + assert_eq!(resp.status(), reqwest::StatusCode::OK); + let endpoint_list: Vec = + serde_json::from_str(&resp.text().await.unwrap()).unwrap(); + let matching_endpoint = endpoint_list.iter().find(|e| { + e["webSocketDebuggerUrl"] + .as_str() + .unwrap() + .contains(expected) + }); + assert!(matching_endpoint.is_some()); + } + child.kill().unwrap(); } diff --git a/runtime/inspector_server.rs b/runtime/inspector_server.rs index 330e91c3a8..70dda9832e 100644 --- a/runtime/inspector_server.rs +++ b/runtime/inspector_server.rs @@ -16,6 +16,7 @@ use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::task::spawn; +use deno_core::url::Url; use deno_core::InspectorMsg; use deno_core::InspectorSessionProxy; use deno_core::JsRuntime; @@ -189,11 +190,12 @@ fn handle_ws_request( fn handle_json_request( inspector_map: Rc>>, + host: Option, ) -> http::Result> { let data = inspector_map .borrow() .values() - .map(|info| info.get_json_metadata()) + .map(move |info| info.get_json_metadata(&host)) .collect::>(); http::Response::builder() .status(http::StatusCode::OK) @@ -224,7 +226,7 @@ async fn server( .map(|info| { eprintln!( "Debugger listening on {}", - info.get_websocket_debugger_url() + info.get_websocket_debugger_url(&info.host.to_string()) ); eprintln!("Visit chrome://inspect to connect to the debugger."); if info.wait_for_session { @@ -258,6 +260,17 @@ async fn server( future::ok::<_, Infallible>(hyper::service::service_fn( move |req: http::Request| { future::ready({ + // If the host header can make a valid URL, use it + let host = req + .headers() + .get("host") + .and_then(|host| host.to_str().ok()) + .and_then(|host| Url::parse(&format!("http://{host}")).ok()) + .and_then(|url| match (url.host(), url.port()) { + (Some(host), Some(port)) => Some(format!("{host}:{port}")), + (Some(host), None) => Some(format!("{host}")), + _ => None, + }); match (req.method(), req.uri().path()) { (&http::Method::GET, path) if path.starts_with("/ws/") => { handle_ws_request(req, Rc::clone(&inspector_map)) @@ -266,10 +279,10 @@ async fn server( handle_json_version_request(json_version_response.clone()) } (&http::Method::GET, "/json") => { - handle_json_request(Rc::clone(&inspector_map)) + handle_json_request(Rc::clone(&inspector_map), host) } (&http::Method::GET, "/json/list") => { - handle_json_request(Rc::clone(&inspector_map)) + handle_json_request(Rc::clone(&inspector_map), host) } _ => http::Response::builder() .status(http::StatusCode::NOT_FOUND) @@ -381,27 +394,29 @@ impl InspectorInfo { } } - fn get_json_metadata(&self) -> Value { + fn get_json_metadata(&self, host: &Option) -> Value { + let host_listen = format!("{}", self.host); + let host = host.as_ref().unwrap_or(&host_listen); json!({ "description": "deno", - "devtoolsFrontendUrl": self.get_frontend_url(), + "devtoolsFrontendUrl": self.get_frontend_url(host), "faviconUrl": "https://deno.land/favicon.ico", "id": self.uuid.to_string(), "title": self.get_title(), "type": "node", "url": self.url.to_string(), - "webSocketDebuggerUrl": self.get_websocket_debugger_url(), + "webSocketDebuggerUrl": self.get_websocket_debugger_url(host), }) } - pub fn get_websocket_debugger_url(&self) -> String { - format!("ws://{}/ws/{}", &self.host, &self.uuid) + pub fn get_websocket_debugger_url(&self, host: &str) -> String { + format!("ws://{}/ws/{}", host, &self.uuid) } - fn get_frontend_url(&self) -> String { + fn get_frontend_url(&self, host: &str) -> String { format!( "devtools://devtools/bundled/js_app.html?ws={}/ws/{}&experiments=true&v8only=true", - &self.host, &self.uuid + host, &self.uuid ) }