diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index a17a8fc247..7b730e455f 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -4,6 +4,7 @@ use crate::colors; use crate::http_cache::HttpCache; use crate::http_util::create_http_client; use crate::http_util::fetch_once; +use crate::http_util::get_user_agent; use crate::http_util::FetchOnceResult; use crate::media_type::MediaType; use crate::permissions::Permissions; @@ -289,7 +290,7 @@ impl FileFetcher { cache: FileCache::default(), cache_setting, http_cache, - http_client: create_http_client(maybe_ca_file)?, + http_client: create_http_client(get_user_agent(), maybe_ca_file)?, }) } diff --git a/cli/http_util.rs b/cli/http_util.rs index c32e0dedae..4bd59e32b0 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -27,14 +27,18 @@ use std::task::Context; use std::task::Poll; use tokio::io::AsyncRead; +pub fn get_user_agent() -> String { + format!("Deno/{}", version::deno()) +} + /// Create new instance of async reqwest::Client. This client supports /// proxies and doesn't follow redirects. -pub fn create_http_client(ca_file: Option<&str>) -> Result { +pub fn create_http_client( + user_agent: String, + ca_file: Option<&str>, +) -> Result { let mut headers = HeaderMap::new(); - headers.insert( - USER_AGENT, - format!("Deno/{}", version::deno()).parse().unwrap(), - ); + headers.insert(USER_AGENT, user_agent.parse().unwrap()); let mut builder = Client::builder() .redirect(Policy::none()) .default_headers(headers) @@ -230,13 +234,17 @@ impl AsyncRead for HttpBody { mod tests { use super::*; + fn create_test_client(ca_file: Option<&str>) -> Client { + create_http_client("test_client".to_string(), ca_file).unwrap() + } + #[tokio::test] async fn test_fetch_string() { let _http_server_guard = test_util::http_server(); // Relies on external http server. See target/debug/test_server let url = Url::parse("http://127.0.0.1:4545/cli/tests/fixture.json").unwrap(); - let client = create_http_client(None).unwrap(); + let client = create_test_client(None); let result = fetch_once(client, &url, None).await; if let Ok(FetchOnceResult::Code(body, headers)) = result { assert!(!body.is_empty()); @@ -256,7 +264,7 @@ mod tests { "http://127.0.0.1:4545/cli/tests/053_import_compression/gziped", ) .unwrap(); - let client = create_http_client(None).unwrap(); + let client = create_test_client(None); let result = fetch_once(client, &url, None).await; if let Ok(FetchOnceResult::Code(body, headers)) = result { assert_eq!(String::from_utf8(body).unwrap(), "console.log('gzip')"); @@ -275,7 +283,7 @@ mod tests { async fn test_fetch_with_etag() { let _http_server_guard = test_util::http_server(); let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); - let client = create_http_client(None).unwrap(); + let client = create_test_client(None); let result = fetch_once(client.clone(), &url, None).await; if let Ok(FetchOnceResult::Code(body, headers)) = result { assert!(!body.is_empty()); @@ -302,7 +310,7 @@ mod tests { "http://127.0.0.1:4545/cli/tests/053_import_compression/brotli", ) .unwrap(); - let client = create_http_client(None).unwrap(); + let client = create_test_client(None); let result = fetch_once(client, &url, None).await; if let Ok(FetchOnceResult::Code(body, headers)) = result { assert!(!body.is_empty()); @@ -327,7 +335,7 @@ mod tests { // Dns resolver substitutes `127.0.0.1` with `localhost` let target_url = Url::parse("http://localhost:4545/cli/tests/fixture.json").unwrap(); - let client = create_http_client(None).unwrap(); + let client = create_test_client(None); let result = fetch_once(client, &url, None).await; if let Ok(FetchOnceResult::Redirect(url, _)) = result { assert_eq!(url, target_url); @@ -381,12 +389,15 @@ mod tests { let url = Url::parse("https://localhost:5545/cli/tests/fixture.json").unwrap(); - let client = create_http_client(Some( - test_util::root_path() - .join("std/http/testdata/tls/RootCA.pem") - .to_str() - .unwrap(), - )) + let client = create_http_client( + get_user_agent(), + Some( + test_util::root_path() + .join("std/http/testdata/tls/RootCA.pem") + .to_str() + .unwrap(), + ), + ) .unwrap(); let result = fetch_once(client, &url, None).await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -407,12 +418,15 @@ mod tests { "https://localhost:5545/cli/tests/053_import_compression/gziped", ) .unwrap(); - let client = create_http_client(Some( - test_util::root_path() - .join("std/http/testdata/tls/RootCA.pem") - .to_str() - .unwrap(), - )) + let client = create_http_client( + get_user_agent(), + Some( + test_util::root_path() + .join("std/http/testdata/tls/RootCA.pem") + .to_str() + .unwrap(), + ), + ) .unwrap(); let result = fetch_once(client, &url, None).await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -432,12 +446,15 @@ mod tests { async fn test_fetch_with_cafile_with_etag() { let _http_server_guard = test_util::http_server(); let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap(); - let client = create_http_client(Some( - test_util::root_path() - .join("std/http/testdata/tls/RootCA.pem") - .to_str() - .unwrap(), - )) + let client = create_http_client( + get_user_agent(), + Some( + test_util::root_path() + .join("std/http/testdata/tls/RootCA.pem") + .to_str() + .unwrap(), + ), + ) .unwrap(); let result = fetch_once(client.clone(), &url, None).await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -466,12 +483,15 @@ mod tests { "https://localhost:5545/cli/tests/053_import_compression/brotli", ) .unwrap(); - let client = create_http_client(Some( - test_util::root_path() - .join("std/http/testdata/tls/RootCA.pem") - .to_str() - .unwrap(), - )) + let client = create_http_client( + get_user_agent(), + Some( + test_util::root_path() + .join("std/http/testdata/tls/RootCA.pem") + .to_str() + .unwrap(), + ), + ) .unwrap(); let result = fetch_once(client, &url, None).await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -493,7 +513,7 @@ mod tests { let _g = test_util::http_server(); let url_str = "http://127.0.0.1:4545/bad_redirect"; let url = Url::parse(url_str).unwrap(); - let client = create_http_client(None).unwrap(); + let client = create_test_client(None); let result = fetch_once(client, &url, None).await; assert!(result.is_err()); let err = result.unwrap_err(); diff --git a/cli/inspector.rs b/cli/inspector.rs index fa1bfa2d04..89fd5bf576 100644 --- a/cli/inspector.rs +++ b/cli/inspector.rs @@ -52,7 +52,7 @@ pub struct InspectorServer { } impl InspectorServer { - pub fn new(host: SocketAddr) -> Self { + pub fn new(host: SocketAddr, name: String) -> Self { let (register_inspector_tx, register_inspector_rx) = mpsc::unbounded::(); @@ -63,6 +63,7 @@ impl InspectorServer { host, register_inspector_rx, shutdown_server_rx, + name, )) }); @@ -145,6 +146,7 @@ async fn server( host: SocketAddr, register_inspector_rx: UnboundedReceiver, shutdown_server_rx: oneshot::Receiver<()>, + name: String, ) { // TODO: put the `inspector_map` in an `Rc>` instead. This is // currently not possible because warp requires all filters to implement @@ -199,13 +201,13 @@ async fn server( ) }); - let json_version_route = warp::path!("json" / "version").map(|| { - warp::reply::json(&json!({ - "Browser": format!("Deno/{}", crate::version::deno()), - "Protocol-Version": "1.3", - "V8-Version": deno_core::v8_version(), - })) + let json_version_response = json!({ + "Browser": name, + "Protocol-Version": "1.3", + "V8-Version": deno_core::v8_version(), }); + let json_version_route = warp::path!("json" / "version") + .map(move || warp::reply::json(&json_version_response)); let inspector_map_ = inspector_map.clone(); let json_list_route = warp::path("json").map(move || { diff --git a/cli/main.rs b/cli/main.rs index 8f8879ac96..38deec5bb8 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -121,6 +121,7 @@ fn create_web_worker_callback( .map_or(false, |l| l == log::Level::Debug), unstable: program_state.flags.unstable, ca_filepath: program_state.flags.ca_file.clone(), + user_agent: http_util::get_user_agent(), seed: program_state.flags.seed, module_loader, create_web_worker_cb, @@ -128,6 +129,9 @@ fn create_web_worker_callback( use_deno_namespace: args.use_deno_namespace, attach_inspector, maybe_inspector_server, + runtime_version: version::deno(), + ts_version: version::TYPESCRIPT.to_string(), + no_color: !colors::use_color(), }; let mut worker = WebWorker::from_options( @@ -192,6 +196,7 @@ pub fn create_main_worker( .map_or(false, |l| l == log::Level::Debug), unstable: program_state.flags.unstable, ca_filepath: program_state.flags.ca_file.clone(), + user_agent: http_util::get_user_agent(), seed: program_state.flags.seed, js_error_create_fn: Some(js_error_create_fn), create_web_worker_cb, @@ -199,6 +204,9 @@ pub fn create_main_worker( maybe_inspector_server, should_break_on_first_statement, module_loader, + runtime_version: version::deno(), + ts_version: version::TYPESCRIPT.to_string(), + no_color: !colors::use_color(), }; let mut worker = MainWorker::from_options(main_module, permissions, &options); diff --git a/cli/ops/fetch.rs b/cli/ops/fetch.rs index 8c1a2b39c5..18e9e9c9f2 100644 --- a/cli/ops/fetch.rs +++ b/cli/ops/fetch.rs @@ -1,4 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use crate::http_util; use crate::permissions::Permissions; use deno_fetch::reqwest; @@ -7,7 +8,8 @@ pub fn init(rt: &mut deno_core::JsRuntime, maybe_ca_file: Option<&str>) { let op_state = rt.op_state(); let mut state = op_state.borrow_mut(); state.put::({ - crate::http_util::create_http_client(maybe_ca_file).unwrap() + http_util::create_http_client(http_util::get_user_agent(), maybe_ca_file) + .unwrap() }); } super::reg_json_async(rt, "op_fetch", deno_fetch::op_fetch::); diff --git a/cli/ops/websocket.rs b/cli/ops/websocket.rs index de6357c87c..a8c591a332 100644 --- a/cli/ops/websocket.rs +++ b/cli/ops/websocket.rs @@ -35,14 +35,21 @@ use webpki::DNSNameRef; #[derive(Clone)] struct WsCaFile(String); +#[derive(Clone)] +struct WsUserAgent(String); -pub fn init(rt: &mut deno_core::JsRuntime, maybe_ca_file: Option<&str>) { +pub fn init( + rt: &mut deno_core::JsRuntime, + maybe_ca_file: Option<&str>, + user_agent: String, +) { { let op_state = rt.op_state(); let mut state = op_state.borrow_mut(); if let Some(ca_file) = maybe_ca_file { state.put::(WsCaFile(ca_file.to_string())); } + state.put::(WsUserAgent(user_agent)); } super::reg_json_sync(rt, "op_ws_check_permission", op_ws_check_permission); super::reg_json_async(rt, "op_ws_create", op_ws_create); @@ -103,11 +110,11 @@ pub async fn op_ws_create( } let maybe_ca_file = state.borrow().try_borrow::().cloned(); + let user_agent = state.borrow().borrow::().0.clone(); let uri: Uri = args.url.parse()?; let mut request = Request::builder().method(Method::GET).uri(&uri); - request = - request.header("User-Agent", format!("Deno/{}", crate::version::deno())); + request = request.header("User-Agent", user_agent); if !args.protocols.is_empty() { request = request.header("Sec-WebSocket-Protocol", args.protocols); diff --git a/cli/program_state.rs b/cli/program_state.rs index 41b7c51fe0..7b86b7de56 100644 --- a/cli/program_state.rs +++ b/cli/program_state.rs @@ -5,6 +5,7 @@ use crate::file_fetcher::CacheSetting; use crate::file_fetcher::FileFetcher; use crate::flags; use crate::http_cache; +use crate::http_util; use crate::import_map::ImportMap; use crate::inspector::InspectorServer; use crate::lockfile::Lockfile; @@ -99,7 +100,10 @@ impl ProgramState { let maybe_inspect_host = flags.inspect.or(flags.inspect_brk); let maybe_inspector_server = match maybe_inspect_host { - Some(host) => Some(Arc::new(InspectorServer::new(host))), + Some(host) => Some(Arc::new(InspectorServer::new( + host, + http_util::get_user_agent(), + ))), None => None, }; diff --git a/cli/standalone.rs b/cli/standalone.rs index d7ffd0fd2e..067fec36a1 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -2,6 +2,7 @@ use crate::colors; use crate::flags::Flags; use crate::permissions::Permissions; use crate::tokio_util; +use crate::version; use crate::worker::MainWorker; use crate::worker::WorkerOptions; use deno_core::error::type_error; @@ -120,6 +121,7 @@ async fn run(source_code: String, args: Vec) -> Result<(), AnyError> { apply_source_maps: false, args: flags.argv.clone(), debug_flag: false, + user_agent: crate::http_util::get_user_agent(), unstable: true, ca_filepath: None, seed: None, @@ -129,6 +131,9 @@ async fn run(source_code: String, args: Vec) -> Result<(), AnyError> { maybe_inspector_server: None, should_break_on_first_statement: false, module_loader, + runtime_version: version::deno(), + ts_version: version::TYPESCRIPT.to_string(), + no_color: !colors::use_color(), }; let mut worker = MainWorker::from_options(main_module.clone(), permissions, &options); diff --git a/cli/web_worker.rs b/cli/web_worker.rs index 9320c22d81..18d391580c 100644 --- a/cli/web_worker.rs +++ b/cli/web_worker.rs @@ -8,7 +8,6 @@ use crate::metrics::Metrics; use crate::ops; use crate::permissions::Permissions; use crate::tokio_util::create_basic_runtime; -use crate::version; use deno_core::error::AnyError; use deno_core::futures::channel::mpsc; use deno_core::futures::future::poll_fn; @@ -133,10 +132,12 @@ pub struct WebWorker { } pub struct WebWorkerOptions { + /// Sets `Deno.args` in JS runtime. pub args: Vec, pub debug_flag: bool, pub unstable: bool, pub ca_filepath: Option, + pub user_agent: String, pub seed: Option, pub module_loader: Rc, pub create_web_worker_cb: Arc, @@ -145,6 +146,12 @@ pub struct WebWorkerOptions { pub attach_inspector: bool, pub maybe_inspector_server: Option>, pub apply_source_maps: bool, + /// Sets `Deno.version.deno` in JS runtime. + pub runtime_version: String, + /// Sets `Deno.version.typescript` in JS runtime. + pub ts_version: String, + /// Sets `Deno.noColor` in JS runtime. + pub no_color: bool, } impl WebWorker { @@ -222,7 +229,11 @@ impl WebWorker { deno_web::op_domain_to_ascii, ); ops::io::init(js_runtime); - ops::websocket::init(js_runtime, options.ca_filepath.as_deref()); + ops::websocket::init( + js_runtime, + options.ca_filepath.as_deref(), + options.user_agent.clone(), + ); if options.use_deno_namespace { ops::fs_events::init(js_runtime); @@ -260,12 +271,12 @@ impl WebWorker { "args": options.args, "applySourceMaps": options.apply_source_maps, "debugFlag": options.debug_flag, - "denoVersion": version::deno(), - "noColor": !colors::use_color(), + "denoVersion": options.runtime_version, + "noColor": options.no_color, "pid": std::process::id(), "ppid": ops::runtime::ppid(), "target": env!("TARGET"), - "tsVersion": version::TYPESCRIPT, + "tsVersion": options.ts_version, "unstableFlag": options.unstable, "v8Version": deno_core::v8_version(), }); @@ -466,6 +477,7 @@ mod tests { debug_flag: false, unstable: false, ca_filepath: None, + user_agent: "x".to_string(), seed: None, module_loader, create_web_worker_cb, @@ -473,6 +485,9 @@ mod tests { use_deno_namespace: false, attach_inspector: false, maybe_inspector_server: None, + runtime_version: "x".to_string(), + ts_version: "x".to_string(), + no_color: true, }; let mut worker = WebWorker::from_options( diff --git a/cli/worker.rs b/cli/worker.rs index b119c3b0dd..d1238df419 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -1,6 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::colors; use crate::inspector::DenoInspector; use crate::inspector::InspectorServer; use crate::inspector::InspectorSession; @@ -8,7 +7,6 @@ use crate::js; use crate::metrics::Metrics; use crate::ops; use crate::permissions::Permissions; -use crate::version; use deno_core::error::AnyError; use deno_core::futures::future::poll_fn; use deno_core::futures::future::FutureExt; @@ -42,10 +40,12 @@ pub struct MainWorker { pub struct WorkerOptions { pub apply_source_maps: bool, + /// Sets `Deno.args` in JS runtime. pub args: Vec, pub debug_flag: bool, pub unstable: bool, pub ca_filepath: Option, + pub user_agent: String, pub seed: Option, pub module_loader: Rc, // Callback that will be invoked when creating new instance @@ -55,6 +55,12 @@ pub struct WorkerOptions { pub attach_inspector: bool, pub maybe_inspector_server: Option>, pub should_break_on_first_statement: bool, + /// Sets `Deno.version.deno` in JS runtime. + pub runtime_version: String, + /// Sets `Deno.version.typescript` in JS runtime. + pub ts_version: String, + /// Sets `Deno.noColor` in JS runtime. + pub no_color: bool, } impl MainWorker { @@ -128,7 +134,11 @@ impl MainWorker { ops::signal::init(js_runtime); ops::tls::init(js_runtime); ops::tty::init(js_runtime); - ops::websocket::init(js_runtime, options.ca_filepath.as_deref()); + ops::websocket::init( + js_runtime, + options.ca_filepath.as_deref(), + options.user_agent.clone(), + ); } { let op_state = js_runtime.op_state(); @@ -154,12 +164,12 @@ impl MainWorker { "args": options.args, "applySourceMaps": options.apply_source_maps, "debugFlag": options.debug_flag, - "denoVersion": version::deno(), - "noColor": !colors::use_color(), + "denoVersion": options.runtime_version, + "noColor": options.no_color, "pid": std::process::id(), "ppid": ops::runtime::ppid(), "target": env!("TARGET"), - "tsVersion": version::TYPESCRIPT, + "tsVersion": options.ts_version, "unstableFlag": options.unstable, "v8Version": deno_core::v8_version(), }); @@ -249,6 +259,7 @@ mod tests { let options = WorkerOptions { apply_source_maps: false, + user_agent: "x".to_string(), args: vec![], debug_flag: false, unstable: false, @@ -260,6 +271,9 @@ mod tests { maybe_inspector_server: None, should_break_on_first_statement: false, module_loader: Rc::new(deno_core::FsModuleLoader), + runtime_version: "x".to_string(), + ts_version: "x".to_string(), + no_color: true, }; MainWorker::from_options(main_module, permissions, &options)