summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLia Lenckowski <lialenck@protonmail.com>2023-08-28 12:42:43 +0200
committermetamuffin <metamuffin@disroot.org>2023-08-28 14:08:03 +0200
commit951c4e90b573f3d14a137bade0853fb3f0f21a5d (patch)
tree0e181b277c08db9ee1075d14ccf9e4249d3be2f3
parent56fb681279b2f2221eef933617d521469c6e6d83 (diff)
downloadgnix-951c4e90b573f3d14a137bade0853fb3f0f21a5d.tar
gnix-951c4e90b573f3d14a137bade0853fb3f0f21a5d.tar.bz2
gnix-951c4e90b573f3d14a137bade0853fb3f0f21a5d.tar.zst
supporting listening on a list of addresses
Signed-off-by: metamuffin <metamuffin@disroot.org>
-rw-r--r--Cargo.lock96
-rw-r--r--Cargo.toml2
-rw-r--r--readme.md3
-rw-r--r--src/config.rs36
-rw-r--r--src/main.rs59
5 files changed, 148 insertions, 48 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ee78064..c8d605a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -144,52 +144,89 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
+name = "futures"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
name = "futures-channel"
-version = "0.3.21"
+version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
dependencies = [
"futures-core",
+ "futures-sink",
]
[[package]]
name = "futures-core"
-version = "0.3.26"
+version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
[[package]]
name = "futures-macro"
-version = "0.3.26"
+version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.29",
]
[[package]]
name = "futures-sink"
-version = "0.3.21"
+version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
[[package]]
name = "futures-task"
-version = "0.3.26"
+version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
[[package]]
name = "futures-util"
-version = "0.3.26"
+version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
+ "futures-channel",
"futures-core",
+ "futures-io",
"futures-macro",
+ "futures-sink",
"futures-task",
+ "memchr",
"pin-project-lite",
"pin-utils",
"slab",
@@ -212,6 +249,7 @@ dependencies = [
"anyhow",
"bytes",
"env_logger",
+ "futures",
"futures-util",
"headers",
"http-body-util",
@@ -221,6 +259,7 @@ dependencies = [
"markup",
"mime_guess",
"percent-encoding",
+ "proc-macro2",
"rustls",
"rustls-pemfile",
"serde",
@@ -487,7 +526,7 @@ checksum = "1a927f0e237dcbdd8c1a8ab03c4e1e8b1999804c448ebf06ff3b5512506c8150"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.107",
]
[[package]]
@@ -592,18 +631,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
-version = "1.0.51"
+version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.18"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@@ -717,7 +756,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.107",
]
[[package]]
@@ -789,6 +828,17 @@ dependencies = [
]
[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -814,7 +864,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.107",
]
[[package]]
@@ -845,7 +895,7 @@ checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.107",
]
[[package]]
@@ -1003,7 +1053,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.107",
"wasm-bindgen-shared",
]
@@ -1025,7 +1075,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 1.0.107",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
diff --git a/Cargo.toml b/Cargo.toml
index 022024f..889da38 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ tokio-rustls = "0.23.4"
tokio = { version = "1.25.0", features = ["full"] }
tokio-util = { version = "0.7.7", features = ["io"] }
futures-util = "0.3.26"
+futures = "0.3.26"
# Config
serde = { version = "1.0.152", features = ["derive"] }
@@ -38,3 +39,4 @@ mime_guess = "2.0.4"
bytes = "1.4.0"
anyhow = "1.0.69"
thiserror = "1.0.38"
+proc-macro2 = "1.0.66"
diff --git a/readme.md b/readme.md
index 3d4eb28..ec25d31 100644
--- a/readme.md
+++ b/readme.md
@@ -17,7 +17,8 @@ configuration file is written in TOML and could look like this:
```toml
# Both the [http] and [https] sections are optional
[http]
-bind = "127.0.0.1:8080"
+# the value for 'bind' can either be a string or a list of strings
+bind = [ "127.0.0.1:8080", "[::1]:8080" ]
[https]
bind = "127.0.0.1:8443"
diff --git a/src/config.rs b/src/config.rs
index 9e542d7..89a2b54 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,6 +1,6 @@
use anyhow::Context;
-use serde::{Deserialize, Serialize};
-use std::{collections::HashMap, fs::read_to_string, net::SocketAddr, path::PathBuf};
+use serde::{Deserialize, Serialize, Deserializer, de::{Visitor, Error, SeqAccess, value}};
+use std::{collections::HashMap, fmt, fs::read_to_string, net::SocketAddr, path::PathBuf};
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
@@ -21,12 +21,14 @@ pub struct Limits {
#[derive(Debug, Serialize, Deserialize)]
pub struct HttpConfig {
- pub bind: SocketAddr,
+ #[serde(deserialize_with = "string_or_seq")]
+ pub bind: Vec<SocketAddr>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HttpsConfig {
- pub bind: SocketAddr,
+ #[serde(deserialize_with = "string_or_seq")]
+ pub bind: Vec<SocketAddr>,
pub tls_cert: PathBuf,
pub tls_key: PathBuf,
}
@@ -45,6 +47,32 @@ pub struct FileserverConfig {
pub index: bool,
}
+// fall back to expecting a single string and putting that in a 1-length vector
+fn string_or_seq<'de, D>(des: D) -> Result<Vec<SocketAddr>, D::Error>
+where D: Deserializer<'de> {
+ struct StringOrList;
+ impl<'de> Visitor<'de> for StringOrList {
+ type Value = Vec<SocketAddr>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("sequence or list")
+ }
+
+ fn visit_str<E>(self, val: &str) -> Result<Vec<SocketAddr>, E>
+ where E: Error {
+ let addr = SocketAddr::deserialize(value::StrDeserializer::new(val))?;
+ Ok(vec![addr])
+ }
+
+ fn visit_seq<A>(self, val: A) -> Result<Vec<SocketAddr>, A::Error>
+ where A: SeqAccess<'de> {
+ Vec::<SocketAddr>::deserialize(value::SeqAccessDeserializer::new(val))
+ }
+ }
+
+ des.deserialize_any(StringOrList)
+}
+
impl Config {
pub fn load(path: &str) -> anyhow::Result<Config> {
let raw = read_to_string(path).context("reading config file")?;
diff --git a/src/main.rs b/src/main.rs
index f51cd67..aa13609 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,6 +14,7 @@ use crate::{
};
use anyhow::{anyhow, bail, Context, Result};
use error::ServiceError;
+use futures::future::try_join_all;
use http_body_util::{combinators::BoxBody, BodyExt};
use hyper::{
body::Incoming,
@@ -88,14 +89,24 @@ async fn serve_http(state: Arc<State>) -> Result<()> {
Some(n) => n,
None => return Ok(()),
};
- let listener = TcpListener::bind(http_config.bind).await?;
+
+ let listen_futs: Result<Vec<()>> = try_join_all(http_config.bind
+ .iter()
+ .map(|e| async {
+ let l = TcpListener::bind(e.clone()).await?;
+ loop {
+ let (stream, addr) = l.accept().await.context("accepting connection")?;
+ debug!("connection from {addr}");
+ let config = state.clone();
+ tokio::spawn(async move { serve_stream(config, stream, addr).await });
+ }
+ }))
+ .await;
+
info!("serving http");
- loop {
- let (stream, addr) = listener.accept().await.context("accepting connection")?;
- debug!("connection from {addr}");
- let config = state.clone();
- tokio::spawn(async move { serve_stream(config, stream, addr).await });
- }
+
+ listen_futs?;
+ Ok(())
}
async fn serve_https(state: Arc<State>) -> Result<()> {
@@ -116,22 +127,30 @@ async fn serve_https(state: Arc<State>) -> Result<()> {
];
Arc::new(cfg)
};
- let listener = TcpListener::bind(https_config.bind).await?;
let tls_acceptor = Arc::new(TlsAcceptor::from(tls_config));
+ let listen_futs: Result<Vec<()>> = try_join_all(https_config.bind
+ .iter()
+ .map(|e| async {
+ let l = TcpListener::bind(e.clone()).await?;
+ loop {
+ let (stream, addr) = l.accept().await.context("accepting connection")?;
+ let state = state.clone();
+ let tls_acceptor = tls_acceptor.clone();
+ tokio::task::spawn(async move {
+ debug!("connection from {addr}");
+ match tls_acceptor.accept(stream).await {
+ Ok(stream) => serve_stream(state, stream, addr).await,
+ Err(e) => warn!("error accepting tls: {e}"),
+ };
+ });
+ }
+ }))
+ .await;
+
info!("serving https");
- loop {
- let (stream, addr) = listener.accept().await.context("accepting connection")?;
- let state = state.clone();
- let tls_acceptor = tls_acceptor.clone();
- tokio::task::spawn(async move {
- debug!("connection from {addr}");
- match tls_acceptor.accept(stream).await {
- Ok(stream) => serve_stream(state, stream, addr).await,
- Err(e) => warn!("error accepting tls: {e}"),
- };
- });
- }
+ listen_futs?;
+ Ok(())
}
pub async fn serve_stream<T: AsyncRead + AsyncWrite + Unpin + Send + 'static>(