diff --git a/examples/Cargo.toml b/examples/Cargo.toml index c25fd954e0..28b14ce672 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -19,6 +19,7 @@ tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } serde_json = "1.0.68" serde = { version = "1.0.140", features = ["derive"] } mime = "0.3.16" +open = "5.0.1" futures-util = "0.3.21" tokio-stream = "0.1.8" prost = "0.12.0" diff --git a/examples/poem/local-server-with-browser/Cargo.toml b/examples/poem/local-server-with-browser/Cargo.toml new file mode 100644 index 0000000000..b0737e34c7 --- /dev/null +++ b/examples/poem/local-server-with-browser/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "local-server-with-browser" +version.workspace = true +edition.workspace = true +publish.workspace = true + +[dependencies] +open.workspace = true +poem.workspace = true +tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } +tracing-subscriber.workspace = true diff --git a/examples/poem/local-server-with-browser/src/main.rs b/examples/poem/local-server-with-browser/src/main.rs new file mode 100644 index 0000000000..0846c14c59 --- /dev/null +++ b/examples/poem/local-server-with-browser/src/main.rs @@ -0,0 +1,58 @@ +use poem::{ + handler, + listener::{Listener, TcpListener}, + Result, Route, Server, +}; + +#[handler] +fn hello() -> String { + "Hello from poem!\n".to_string() +} + +#[tokio::main] +async fn main() -> Result<(), std::io::Error> { + let app = Route::new().at("/", poem::get(hello)); + if std::env::var_os("RUST_LOG").is_none() { + std::env::set_var("RUST_LOG", "poem=debug"); + } + tracing_subscriber::fmt::init(); + + // To test port assignment, run two instances of this example at once. + // + // For ports <1024, running with administrator priveleges would be needed on + // Unix. For port 0, the OS would assign a port and we'd need to find out + // what that port's number is. + let (min_port, max_port) = (8080, 8085); + // Using 127.0.0.1 instead of 0.0.0.0 for security; a local server should + // not, generally, be visible from the network. + let hostname = "127.0.0.1"; + let mut port = min_port; + let mut error = None; + let acceptor = loop { + if port > max_port { + return Err(error.unwrap()); + } + let listener = TcpListener::bind(format!("{hostname}:{}", port)); + match listener.into_acceptor().await { + Ok(a) => break a, + Err(err) => { + // Most likely, another application is bound to this port. + eprintln!("Couldn't bind to port {port}."); + error = Some(err) + } + }; + port += 1; + }; + + // Now that the acceptor exists, the browser should be able to connect IIUC. + let http_address = format!("http://{hostname}:{port}/"); + eprintln!("Listening at {http_address}."); + eprint!("Trying to launch a browser at {http_address}..."); + match open::that(&http_address) { + Ok(_) => eprintln!(" Success!"), + Err(err) => eprintln!("\nFailed to launch a browser: {err}"), + } + + Server::new_with_acceptor(acceptor).run(app).await?; + Ok(()) +}