summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Mastracci <matthew@mastracci.com>2023-08-15 16:30:33 -0600
committerGitHub <noreply@github.com>2023-08-15 22:30:33 +0000
commit71d2f4cb97b6cc85cf58c632ecde05cc262ff44f (patch)
tree54f20ea902584201fa2fc69f0aa8776f54ecc3a1
parent4380a09a0598c73aa434e2f0f3a34555e0bd55cb (diff)
fix(runtime): use host header for inspector websocket URL (#20171)
If a `host` header is specified, use that for the generated websocket URLs. Fixes #20087
-rw-r--r--cli/tests/integration/inspector_tests.rs38
-rw-r--r--runtime/inspector_server.rs37
2 files changed, 56 insertions, 19 deletions
diff --git a/cli/tests/integration/inspector_tests.rs b/cli/tests/integration/inspector_tests.rs
index 6b2deb0bf..79422ee5a 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<deno_core::serde_json::Value> =
- 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<deno_core::serde_json::Value> =
+ 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 330e91c3a..70dda9832 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<RefCell<HashMap<Uuid, InspectorInfo>>>,
+ host: Option<String>,
) -> http::Result<http::Response<hyper::Body>> {
let data = inspector_map
.borrow()
.values()
- .map(|info| info.get_json_metadata())
+ .map(move |info| info.get_json_metadata(&host))
.collect::<Vec<_>>();
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<hyper::Body>| {
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<String>) -> 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
)
}