feat(serve): support --port 0 to use an open port (#23846)

Closes https://github.com/denoland/deno/issues/23845
This commit is contained in:
Satya Rohith 2024-05-17 11:08:50 +05:30 committed by GitHub
parent 2b560be83f
commit 20cb0e8863
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 96 additions and 16 deletions

1
Cargo.lock generated
View File

@ -705,6 +705,7 @@ dependencies = [
"once_cell",
"os_pipe",
"pretty_assertions",
"regex",
"serde",
"test_server",
"tokio",

View File

@ -26,7 +26,6 @@ use serde::Serialize;
use std::env;
use std::ffi::OsString;
use std::net::SocketAddr;
use std::num::NonZeroU16;
use std::num::NonZeroU32;
use std::num::NonZeroU8;
use std::num::NonZeroUsize;
@ -283,7 +282,7 @@ impl RunFlags {
pub struct ServeFlags {
pub script: String,
pub watch: Option<WatchFlagsWithPaths>,
pub port: NonZeroU16,
pub port: u16,
pub host: String,
}
@ -293,7 +292,7 @@ impl ServeFlags {
Self {
script,
watch: None,
port: NonZeroU16::new(port).unwrap(),
port,
host: host.to_owned(),
}
}
@ -2464,8 +2463,8 @@ fn serve_subcommand() -> Command {
.arg(
Arg::new("port")
.long("port")
.help("The TCP port to serve on, defaulting to 8000.")
.value_parser(value_parser!(NonZeroU16)),
.help("The TCP port to serve on, defaulting to 8000. Pass 0 to pick a random free port.")
.value_parser(value_parser!(u16)),
)
.arg(
Arg::new("host")
@ -4127,9 +4126,7 @@ fn serve_parse(
app: Command,
) -> clap::error::Result<()> {
// deno serve implies --allow-net=host:port
let port = matches
.remove_one::<NonZeroU16>("port")
.unwrap_or(NonZeroU16::new(8000).unwrap());
let port = matches.remove_one::<u16>("port").unwrap_or(8000);
let host = matches
.remove_one::<String>("host")
.unwrap_or_else(|| "0.0.0.0".to_owned());
@ -5322,6 +5319,32 @@ mod tests {
..Flags::default()
}
);
let r = flags_from_vec(svec![
"deno",
"serve",
"--port",
"0",
"--host",
"example.com",
"main.ts"
]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Serve(ServeFlags::new_default(
"main.ts".to_string(),
0,
"example.com"
)),
permissions: PermissionFlags {
allow_net: Some(vec!["example.com:0".to_owned()]),
..Default::default()
},
code_cache_enabled: true,
..Flags::default()
}
);
}
#[test]

View File

@ -62,7 +62,6 @@ use std::env;
use std::io::BufReader;
use std::io::Cursor;
use std::net::SocketAddr;
use std::num::NonZeroU16;
use std::num::NonZeroUsize;
use std::path::Path;
use std::path::PathBuf;
@ -1036,7 +1035,7 @@ impl CliOptions {
}
}
pub fn serve_port(&self) -> Option<NonZeroU16> {
pub fn serve_port(&self) -> Option<u16> {
if let DenoSubcommand::Serve(flags) = self.sub_command() {
Some(flags.port)
} else {

View File

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::num::NonZeroU16;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
@ -148,7 +147,7 @@ struct SharedWorkerState {
disable_deprecated_api_warning: bool,
verbose_deprecated_api_warning: bool,
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
serve_port: Option<NonZeroU16>,
serve_port: Option<u16>,
serve_host: Option<String>,
}
@ -418,7 +417,7 @@ impl CliMainWorkerFactory {
feature_checker: Arc<FeatureChecker>,
options: CliMainWorkerOptions,
node_ipc: Option<i64>,
serve_port: Option<NonZeroU16>,
serve_port: Option<u16>,
serve_host: Option<String>,
enable_future_features: bool,
disable_deprecated_api_warning: bool,

View File

@ -4,7 +4,6 @@ use deno_core::v8;
use deno_core::ModuleSpecifier;
use serde::Serialize;
use std::cell::RefCell;
use std::num::NonZeroU16;
use std::thread;
use deno_terminal::colors;
@ -93,7 +92,7 @@ pub struct BootstrapOptions {
pub future: bool,
pub mode: WorkerExecutionMode,
// Used by `deno serve`
pub serve_port: Option<NonZeroU16>,
pub serve_port: Option<u16>,
pub serve_host: Option<String>,
}
@ -198,7 +197,7 @@ impl BootstrapOptions {
self.verbose_deprecated_api_warning,
self.future,
self.mode as u8 as _,
self.serve_port.map(|x| x.into()).unwrap_or_default(),
self.serve_port.unwrap_or_default(),
self.serve_host.as_deref(),
);

View File

@ -52,6 +52,7 @@ hyper-util.workspace = true
once_cell.workspace = true
os_pipe.workspace = true
pretty_assertions.workspace = true
regex.workspace = true
serde.workspace = true
test_util.workspace = true
tokio.workspace = true

View File

@ -60,6 +60,8 @@ mod publish;
mod repl;
#[path = "run_tests.rs"]
mod run;
#[path = "serve_tests.rs"]
mod serve;
#[path = "shared_library_tests.rs"]
mod shared_library_tests;
#[path = "task_tests.rs"]

View File

@ -0,0 +1,51 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::io::Read;
use deno_fetch::reqwest;
use pretty_assertions::assert_eq;
use regex::Regex;
use test_util as util;
#[tokio::test]
async fn deno_serve_port_0() {
let mut child = util::deno_cmd()
.current_dir(util::testdata_path())
.arg("serve")
.arg("--port")
.arg("0")
.arg("./serve.ts")
.stdout_piped()
.spawn()
.unwrap();
let stdout = child.stdout.as_mut().unwrap();
let mut buffer = [0; 50];
let _read = stdout.read(&mut buffer).unwrap();
let msg = std::str::from_utf8(&buffer).unwrap();
let port_regex = Regex::new(r"(\d+)").unwrap();
let port = port_regex.find(msg).unwrap().as_str();
let cert = reqwest::Certificate::from_pem(include_bytes!(
"../testdata/tls/RootCA.crt"
))
.unwrap();
let client = reqwest::Client::builder()
.add_root_certificate(cert)
.http2_prior_knowledge()
.build()
.unwrap();
let res = client
.get(&format!("http://127.0.0.1:{port}"))
.send()
.await
.unwrap();
assert_eq!(200, res.status());
let body = res.text().await.unwrap();
assert_eq!(body, "deno serve --port 0 works!");
child.kill().unwrap();
child.wait().unwrap();
}

5
tests/testdata/serve.ts vendored Normal file
View File

@ -0,0 +1,5 @@
export default {
fetch(_req: Request) {
return new Response("deno serve --port 0 works!");
},
};