feat: support ipv6 (#25)
This commit is contained in:
parent
7481db5071
commit
63a7b530bb
2 changed files with 36 additions and 32 deletions
25
src/args.rs
25
src/args.rs
|
@ -1,7 +1,7 @@
|
||||||
use clap::crate_description;
|
use clap::crate_description;
|
||||||
use clap::{Arg, ArgMatches};
|
use clap::{Arg, ArgMatches};
|
||||||
use rustls::{Certificate, PrivateKey};
|
use rustls::{Certificate, PrivateKey};
|
||||||
use std::net::SocketAddr;
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{env, fs, io};
|
use std::{env, fs, io};
|
||||||
|
|
||||||
|
@ -111,8 +111,7 @@ pub fn matches() -> ArgMatches {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
pub address: String,
|
pub addr: SocketAddr,
|
||||||
pub port: u16,
|
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub path_prefix: String,
|
pub path_prefix: String,
|
||||||
pub uri_prefix: String,
|
pub uri_prefix: String,
|
||||||
|
@ -133,8 +132,9 @@ impl Args {
|
||||||
/// If a parsing error ocurred, exit the process and print out informative
|
/// If a parsing error ocurred, exit the process and print out informative
|
||||||
/// error message to user.
|
/// error message to user.
|
||||||
pub fn parse(matches: ArgMatches) -> BoxResult<Args> {
|
pub fn parse(matches: ArgMatches) -> BoxResult<Args> {
|
||||||
let address = matches.value_of("address").unwrap_or_default().to_owned();
|
let ip = matches.value_of("address").unwrap_or_default();
|
||||||
let port = matches.value_of_t::<u16>("port")?;
|
let port = matches.value_of_t::<u16>("port")?;
|
||||||
|
let addr = to_addr(ip, port)?;
|
||||||
let path = Args::parse_path(matches.value_of_os("path").unwrap_or_default())?;
|
let path = Args::parse_path(matches.value_of_os("path").unwrap_or_default())?;
|
||||||
let path_prefix = matches
|
let path_prefix = matches
|
||||||
.value_of("path-prefix")
|
.value_of("path-prefix")
|
||||||
|
@ -166,8 +166,7 @@ impl Args {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Args {
|
Ok(Args {
|
||||||
address,
|
addr,
|
||||||
port,
|
|
||||||
path,
|
path,
|
||||||
path_prefix,
|
path_prefix,
|
||||||
uri_prefix,
|
uri_prefix,
|
||||||
|
@ -197,17 +196,15 @@ impl Args {
|
||||||
})
|
})
|
||||||
.map_err(|err| format!("Failed to access path `{}`: {}", path.display(), err,).into())
|
.map_err(|err| format!("Failed to access path `{}`: {}", path.display(), err,).into())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct socket address from arguments.
|
fn to_addr(ip: &str, port: u16) -> BoxResult<SocketAddr> {
|
||||||
pub fn address(&self) -> BoxResult<SocketAddr> {
|
let ip: IpAddr = ip.parse()?;
|
||||||
format!("{}:{}", self.address, self.port)
|
Ok(SocketAddr::new(ip, port))
|
||||||
.parse()
|
|
||||||
.map_err(|_| format!("Invalid bind address `{}:{}`", self.address, self.port).into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load public certificate from file.
|
// Load public certificate from file.
|
||||||
pub fn load_certs(filename: &str) -> BoxResult<Vec<Certificate>> {
|
fn load_certs(filename: &str) -> BoxResult<Vec<Certificate>> {
|
||||||
// Open certificate file.
|
// Open certificate file.
|
||||||
let certfile =
|
let certfile =
|
||||||
fs::File::open(&filename).map_err(|e| format!("Failed to open {}: {}", &filename, e))?;
|
fs::File::open(&filename).map_err(|e| format!("Failed to open {}: {}", &filename, e))?;
|
||||||
|
@ -222,7 +219,7 @@ pub fn load_certs(filename: &str) -> BoxResult<Vec<Certificate>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load private key from file.
|
// Load private key from file.
|
||||||
pub fn load_private_key(filename: &str) -> BoxResult<PrivateKey> {
|
fn load_private_key(filename: &str) -> BoxResult<PrivateKey> {
|
||||||
// Open keyfile.
|
// Open keyfile.
|
||||||
let keyfile =
|
let keyfile =
|
||||||
fs::File::open(&filename).map_err(|e| format!("Failed to open {}: {}", &filename, e))?;
|
fs::File::open(&filename).map_err(|e| format!("Failed to open {}: {}", &filename, e))?;
|
||||||
|
|
|
@ -25,7 +25,7 @@ use rustls::ServerConfig;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::fs::Metadata;
|
use std::fs::Metadata;
|
||||||
use std::net::IpAddr;
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
@ -56,7 +56,6 @@ macro_rules! status {
|
||||||
|
|
||||||
pub async fn serve(args: Args) -> BoxResult<()> {
|
pub async fn serve(args: Args) -> BoxResult<()> {
|
||||||
let args = Arc::new(args);
|
let args = Arc::new(args);
|
||||||
let socket_addr = args.address()?;
|
|
||||||
let inner = Arc::new(InnerService::new(args.clone()));
|
let inner = Arc::new(InnerService::new(args.clone()));
|
||||||
match args.tls.clone() {
|
match args.tls.clone() {
|
||||||
Some((certs, key)) => {
|
Some((certs, key)) => {
|
||||||
|
@ -66,7 +65,7 @@ pub async fn serve(args: Args) -> BoxResult<()> {
|
||||||
.with_single_cert(certs, key)?;
|
.with_single_cert(certs, key)?;
|
||||||
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
|
let tls_acceptor = TlsAcceptor::from(Arc::new(config));
|
||||||
let arc_acceptor = Arc::new(tls_acceptor);
|
let arc_acceptor = Arc::new(tls_acceptor);
|
||||||
let listener = TcpListener::bind(&socket_addr).await?;
|
let listener = TcpListener::bind(&args.addr).await?;
|
||||||
let incoming = tokio_stream::wrappers::TcpListenerStream::new(listener);
|
let incoming = tokio_stream::wrappers::TcpListenerStream::new(listener);
|
||||||
let incoming =
|
let incoming =
|
||||||
hyper::server::accept::from_stream(incoming.filter_map(|socket| async {
|
hyper::server::accept::from_stream(incoming.filter_map(|socket| async {
|
||||||
|
@ -87,11 +86,11 @@ pub async fn serve(args: Args) -> BoxResult<()> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
print_listening(args.address.as_str(), args.port, &args.uri_prefix, true);
|
print_listening(&args.addr, &args.uri_prefix, true);
|
||||||
server.await?;
|
server.await?;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let server = hyper::Server::try_bind(&socket_addr)?.serve(make_service_fn(move |_| {
|
let server = hyper::Server::try_bind(&args.addr)?.serve(make_service_fn(move |_| {
|
||||||
let inner = inner.clone();
|
let inner = inner.clone();
|
||||||
async move {
|
async move {
|
||||||
Ok::<_, Infallible>(service_fn(move |req| {
|
Ok::<_, Infallible>(service_fn(move |req| {
|
||||||
|
@ -100,7 +99,7 @@ pub async fn serve(args: Args) -> BoxResult<()> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
print_listening(args.address.as_str(), args.port, &args.uri_prefix, false);
|
print_listening(&args.addr, &args.uri_prefix, false);
|
||||||
server.await?;
|
server.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -974,37 +973,45 @@ fn to_content_range(range: &Range, complete_length: u64) -> Option<ContentRange>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_listening(address: &str, port: u16, prefix: &str, tls: bool) {
|
fn print_listening(addr: &SocketAddr, prefix: &str, tls: bool) {
|
||||||
let prefix = encode_uri(prefix.trim_end_matches('/'));
|
let prefix = encode_uri(prefix.trim_end_matches('/'));
|
||||||
let addrs = retrieve_listening_addrs(address);
|
let addrs = retrieve_listening_addrs(addr);
|
||||||
let protocol = if tls { "https" } else { "http" };
|
let protocol = if tls { "https" } else { "http" };
|
||||||
if addrs.len() == 1 {
|
if addrs.len() == 1 {
|
||||||
eprintln!(
|
eprintln!("Listening on {}://{}{}", protocol, addr, prefix);
|
||||||
"Listening on {}://{}:{}{}",
|
|
||||||
protocol, addrs[0], port, prefix
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Listening on:");
|
eprintln!("Listening on:");
|
||||||
for addr in addrs {
|
for addr in addrs {
|
||||||
eprintln!(" {}://{}:{}{}", protocol, addr, port, prefix);
|
eprintln!(" {}://{}{}", protocol, addr, prefix);
|
||||||
}
|
}
|
||||||
eprintln!();
|
eprintln!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn retrieve_listening_addrs(address: &str) -> Vec<String> {
|
fn retrieve_listening_addrs(addr: &SocketAddr) -> Vec<SocketAddr> {
|
||||||
if address == "0.0.0.0" {
|
let ip = addr.ip();
|
||||||
|
let port = addr.port();
|
||||||
|
if ip.is_unspecified() {
|
||||||
if let Ok(interfaces) = get_if_addrs() {
|
if let Ok(interfaces) = get_if_addrs() {
|
||||||
let mut ifaces: Vec<IpAddr> = interfaces
|
let mut ifaces: Vec<IpAddr> = interfaces
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| v.ip())
|
.map(|v| v.ip())
|
||||||
.filter(|v| v.is_ipv4())
|
.filter(|v| {
|
||||||
|
if ip.is_ipv4() {
|
||||||
|
v.is_ipv4()
|
||||||
|
} else {
|
||||||
|
v.is_ipv6()
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
ifaces.sort();
|
ifaces.sort();
|
||||||
return ifaces.into_iter().map(|v| v.to_string()).collect();
|
return ifaces
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| SocketAddr::new(v, port))
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vec![address.to_owned()]
|
vec![addr.to_owned()]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_uri(v: &str) -> String {
|
fn encode_uri(v: &str) -> String {
|
||||||
|
|
Loading…
Reference in a new issue