Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 30 additions & 7 deletions cli/args/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
}

Expand All @@ -293,7 +292,7 @@ impl ServeFlags {
Self {
script,
watch: None,
port: NonZeroU16::new(port).unwrap(),
port,
host: host.to_owned(),
}
}
Expand Down Expand Up @@ -2465,7 +2464,7 @@ fn serve_subcommand() -> Command {
Arg::new("port")
.long("port")
.help("The TCP port to serve on, defaulting to 8000.")
.value_parser(value_parser!(NonZeroU16)),
.value_parser(value_parser!(u16)),
)
.arg(
Arg::new("host")
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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]
Expand Down
3 changes: 1 addition & 2 deletions cli/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 2 additions & 3 deletions cli/worker.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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>,
}

Expand Down Expand Up @@ -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,
Expand Down
5 changes: 2 additions & 3 deletions runtime/worker_bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>,
}

Expand Down Expand Up @@ -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(),
);

Expand Down
1 change: 1 addition & 0 deletions tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
51 changes: 51 additions & 0 deletions tests/integration/serve_tests.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 5 additions & 0 deletions tests/testdata/serve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
fetch(_req: Request) {
return new Response("deno serve --port 0 works!");
},
};