From ded7549c58cd2d91c023daeba120226c9498acdf Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Thu, 22 Oct 2020 18:08:49 +0200 Subject: [PATCH 1/9] fix(http): create Profile abstraction to allow port choice --- http/src/config.rs | 58 ++++++++++++++++++++++++++++++++-------------- http/src/main.rs | 25 +++++++++++++------- 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/http/src/config.rs b/http/src/config.rs index 24abe3461..d95f1d484 100644 --- a/http/src/config.rs +++ b/http/src/config.rs @@ -3,8 +3,11 @@ use parity_multiaddr::Multiaddr; use serde::{Deserialize, Serialize}; use std::fs::{self, File}; +use std::net::SocketAddr; use std::num::NonZeroU16; use std::path::Path; +use std::str::FromStr; +use structopt::StructOpt; use thiserror::Error; /// Temporary module required to de/ser config files base64'd protobuf rsa private key format. @@ -13,6 +16,24 @@ mod keys_proto { include!(concat!(env!("OUT_DIR"), "/keys_proto.rs")); } +#[derive(Debug, StructOpt)] +pub enum Profile { + Test, + Default, +} + +impl FromStr for Profile { + type Err = InitializationError; + + fn from_str(profile: &str) -> Result { + match profile { + "test" => Ok(Profile::Test), + "default" => Ok(Profile::Default), + _ => Err(InitializationError::InvalidProfile(profile.to_string())), + } + } +} + /// The way things can go wrong when calling [`initialize`]. #[derive(Error, Debug)] pub enum InitializationError { @@ -23,7 +44,7 @@ pub enum InitializationError { #[error("invalid RSA key length given: {0}")] InvalidRsaKeyLength(u16), #[error("unsupported profiles selected: {0:?}")] - InvalidProfiles(Vec), + InvalidProfile(String), #[error("key generation failed: {0}")] KeyGeneration(Box), #[error("key encoding failed: {0}")] @@ -37,7 +58,7 @@ pub enum InitializationError { pub fn initialize( ipfs_path: &Path, bits: NonZeroU16, - profiles: Vec, + profile: Profile, ) -> Result<(), InitializationError> { let config_path = ipfs_path.join("config"); @@ -46,14 +67,10 @@ pub fn initialize( .and_then(|_| { fs::File::create(&config_path).map_err(InitializationError::ConfigCreationFailed) }) - .and_then(|config_file| create(config_file, bits, profiles)) + .and_then(|config_file| create(config_file, bits, profile)) } -fn create( - config: File, - bits: NonZeroU16, - profiles: Vec, -) -> Result<(), InitializationError> { +fn create(config: File, bits: NonZeroU16, profile: Profile) -> Result<(), InitializationError> { use multibase::Base::Base64Pad; use prost::Message; use std::io::BufWriter; @@ -65,13 +82,18 @@ fn create( return Err(InitializationError::InvalidRsaKeyLength(bits)); } - if profiles.len() != 1 || profiles[0] != "test" { - // profiles are expected to be (comma separated) "test" as there are no bootstrap peer - // handling yet. the conformance test cases seem to init `go-ipfs` in this profile where - // it does not have any bootstrap nodes, and multi node tests later call swarm apis to - // dial the nodes together. - return Err(InitializationError::InvalidProfiles(profiles)); - } + // if profiles.len() != 1 || profiles[0] != "test" || profiles[0] != "default" { + // // profiles are expected to be (comma separated) "test" as there are no bootstrap peer + // // handling yet. the conformance test cases seem to init `go-ipfs` in this profile where + // // it does not have any bootstrap nodes, and multi node tests later call swarm apis to + // // dial the nodes together. + // return Err(InitializationError::InvalidProfile(profiles)); + // } + + let api_addrs = match profile { + Profile::Test => "127.0.0.1:0", + Profile::Default => "127.0.0.1:4004", + }; let pk = openssl::rsa::Rsa::generate(bits as u32) .map_err(|e| InitializationError::KeyGeneration(Box::new(e)))?; @@ -118,6 +140,7 @@ fn create( }, addresses: Addresses { swarm: vec!["/ip4/127.0.0.1/tcp/0".parse().unwrap()], + api: api_addrs.parse().unwrap(), }, }; @@ -147,7 +170,7 @@ pub enum LoadingError { /// Returns only the keypair and listening addresses or [`LoadingError`] but this should be /// extended to contain the bootstrap nodes at least later when we need to support those for /// testing purposes. -pub fn load(config: File) -> Result<(ipfs::Keypair, Vec), LoadingError> { +pub fn load(config: File) -> Result<(ipfs::Keypair, Vec, SocketAddr), LoadingError> { use std::io::BufReader; let CompatibleConfigFile { @@ -167,7 +190,7 @@ pub fn load(config: File) -> Result<(ipfs::Keypair, Vec), LoadingErro }); } - Ok((kp, addresses.swarm)) + Ok((kp, addresses.swarm, addresses.api)) } /// Converts a PEM format to DER where PEM is a container for Base64 data with padding, starting on @@ -245,6 +268,7 @@ struct CompatibleConfigFile { #[serde(rename_all = "PascalCase")] struct Addresses { swarm: Vec, + api: SocketAddr, } #[derive(Debug, Serialize, Deserialize)] diff --git a/http/src/main.rs b/http/src/main.rs index cc4252f35..da85db0e2 100644 --- a/http/src/main.rs +++ b/http/src/main.rs @@ -17,8 +17,8 @@ enum Options { #[structopt(long)] bits: NonZeroU16, /// List of configuration profiles to apply - #[structopt(long, use_delimiter = true)] - profile: Vec, + #[structopt(long)] + profile: config::Profile, }, /// Start the IPFS node in the foreground (not detaching from parent process). Daemon, @@ -59,7 +59,7 @@ fn main() { let config_path = home.join("config"); - let (keypair, listening_addrs) = match opts { + let (keypair, listening_addrs, api_listening_addrs) = match opts { Options::Init { bits, profile } => { println!("initializing IPFS node at {:?}", home); @@ -73,7 +73,7 @@ fn main() { match result { Ok(_) => { - let (kp, _) = std::fs::File::open(config_path) + let (kp, _, _) = std::fs::File::open(config_path) .map_err(config::LoadingError::ConfigurationFileOpening) .and_then(config::load) .unwrap(); @@ -101,8 +101,8 @@ fn main() { eprintln!("This is a fake version of ipfs cli which does not support much"); std::process::exit(1); } - Err(config::InitializationError::InvalidProfiles(profiles)) => { - eprintln!("Error: unsupported profile selection: {:?}", profiles); + Err(config::InitializationError::InvalidProfile(profile)) => { + eprintln!("Error: unsupported profile selection: {:?}", profile); eprintln!("This is a fake version of ipfs cli which does not support much"); std::process::exit(1); } @@ -153,7 +153,13 @@ fn main() { tokio::spawn(task); let api_link_file = home.join("api"); - let (addr, server) = serve(&ipfs); + + // If the port is specified, use that to start the server, if it isn't use ephemeral ports, + // i.e.: + // + // port => port + // none => 0 + let (addr, server) = serve(&ipfs, api_listening_addrs); // shutdown future will handle signalling the exit drop(ipfs); @@ -185,6 +191,7 @@ fn main() { fn serve( ipfs: &Ipfs, + listening_addrs: std::net::SocketAddr, ) -> (std::net::SocketAddr, impl std::future::Future) { use tokio::stream::StreamExt; use warp::Filter; @@ -195,7 +202,9 @@ fn serve( let ipfs = ipfs.clone(); - warp::serve(routes).bind_with_graceful_shutdown(([127, 0, 0, 1], 0), async move { + println!("SOCKET_ADDR: {:?}", listening_addrs); + + warp::serve(routes).bind_with_graceful_shutdown(listening_addrs, async move { shutdown_rx.next().await; info!("Shutdown trigger received; starting shutdown"); ipfs.exit_daemon().await; From 9dc0c33546bbe027469da29f431cd844993d1aa5 Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Fri, 23 Oct 2020 13:38:01 +0200 Subject: [PATCH 2/9] fix(http): plan for future support of multiple profiles --- http/src/config.rs | 36 ++++++++++++++++++++---------------- http/src/main.rs | 11 ++++++----- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/http/src/config.rs b/http/src/config.rs index d95f1d484..e32758c86 100644 --- a/http/src/config.rs +++ b/http/src/config.rs @@ -58,7 +58,7 @@ pub enum InitializationError { pub fn initialize( ipfs_path: &Path, bits: NonZeroU16, - profile: Profile, + profiles: Vec, ) -> Result<(), InitializationError> { let config_path = ipfs_path.join("config"); @@ -67,10 +67,14 @@ pub fn initialize( .and_then(|_| { fs::File::create(&config_path).map_err(InitializationError::ConfigCreationFailed) }) - .and_then(|config_file| create(config_file, bits, profile)) + .and_then(|config_file| create(config_file, bits, profiles)) } -fn create(config: File, bits: NonZeroU16, profile: Profile) -> Result<(), InitializationError> { +fn create( + config: File, + bits: NonZeroU16, + profiles: Vec, +) -> Result<(), InitializationError> { use multibase::Base::Base64Pad; use prost::Message; use std::io::BufWriter; @@ -82,19 +86,6 @@ fn create(config: File, bits: NonZeroU16, profile: Profile) -> Result<(), Initia return Err(InitializationError::InvalidRsaKeyLength(bits)); } - // if profiles.len() != 1 || profiles[0] != "test" || profiles[0] != "default" { - // // profiles are expected to be (comma separated) "test" as there are no bootstrap peer - // // handling yet. the conformance test cases seem to init `go-ipfs` in this profile where - // // it does not have any bootstrap nodes, and multi node tests later call swarm apis to - // // dial the nodes together. - // return Err(InitializationError::InvalidProfile(profiles)); - // } - - let api_addrs = match profile { - Profile::Test => "127.0.0.1:0", - Profile::Default => "127.0.0.1:4004", - }; - let pk = openssl::rsa::Rsa::generate(bits as u32) .map_err(|e| InitializationError::KeyGeneration(Box::new(e)))?; @@ -133,6 +124,19 @@ fn create(config: File, bits: NonZeroU16, profile: Profile) -> Result<(), Initia let private_key = Base64Pad.encode(&private_key); + if profiles.len() != 1 { + // profiles are expected to be (comma separated) "test" as there are no bootstrap peer + // handling yet. the conformance test cases seem to init `go-ipfs` in this profile where + // it does not have any bootstrap nodes, and multi node tests later call swarm apis to + // dial the nodes together. + unimplemented!("Multiple profiles are currently unsupported!") + } + + let api_addrs = match profiles[0] { + Profile::Test => "127.0.0.1:0", + Profile::Default => "127.0.0.1:4004", + }; + let config_contents = CompatibleConfigFile { identity: Identity { peer_id, diff --git a/http/src/main.rs b/http/src/main.rs index da85db0e2..ba1d6a705 100644 --- a/http/src/main.rs +++ b/http/src/main.rs @@ -16,9 +16,12 @@ enum Options { /// Generated key length #[structopt(long)] bits: NonZeroU16, - /// List of configuration profiles to apply - #[structopt(long)] - profile: config::Profile, + /// List of configuration profiles to apply. Currently only the `Test` and `Default` + /// profiles are supported. + /// + /// `Test` uses ephemeral ports, `Default` uses `4004`. + #[structopt(long, use_delimiter = true)] + profile: Vec, }, /// Start the IPFS node in the foreground (not detaching from parent process). Daemon, @@ -202,8 +205,6 @@ fn serve( let ipfs = ipfs.clone(); - println!("SOCKET_ADDR: {:?}", listening_addrs); - warp::serve(routes).bind_with_graceful_shutdown(listening_addrs, async move { shutdown_rx.next().await; info!("Shutdown trigger received; starting shutdown"); From d4f475d0e14f9346b21ac7f6617b008e96a5864c Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Fri, 23 Oct 2020 14:34:34 +0200 Subject: [PATCH 3/9] fix(http): check for single profile before creating dir + config --- http/src/config.rs | 29 ++++++++++++++++------------- http/src/main.rs | 7 +------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/http/src/config.rs b/http/src/config.rs index e32758c86..4ba358139 100644 --- a/http/src/config.rs +++ b/http/src/config.rs @@ -22,6 +22,7 @@ pub enum Profile { Default, } +// Required for structopt. impl FromStr for Profile { type Err = InitializationError; @@ -60,6 +61,16 @@ pub fn initialize( bits: NonZeroU16, profiles: Vec, ) -> Result<(), InitializationError> { + // This check is done here to avoid an empty config file being created in the case of an + // unsupported input. + if profiles.len() != 1 { + // profiles are expected to be (comma separated) "test" as there are no bootstrap peer + // handling yet. the conformance test cases seem to init `go-ipfs` in this profile where + // it does not have any bootstrap nodes, and multi node tests later call swarm apis to + // dial the nodes together. + unimplemented!("Multiple profiles are currently unsupported!"); + } + let config_path = ipfs_path.join("config"); fs::create_dir_all(&ipfs_path) @@ -79,6 +90,11 @@ fn create( use prost::Message; use std::io::BufWriter; + let api_addrs = match profiles[0] { + Profile::Test => "127.0.0.1:0", + Profile::Default => "127.0.0.1:4004", + }; + let bits = bits.get(); if bits < 2048 || bits > 16 * 1024 { @@ -124,19 +140,6 @@ fn create( let private_key = Base64Pad.encode(&private_key); - if profiles.len() != 1 { - // profiles are expected to be (comma separated) "test" as there are no bootstrap peer - // handling yet. the conformance test cases seem to init `go-ipfs` in this profile where - // it does not have any bootstrap nodes, and multi node tests later call swarm apis to - // dial the nodes together. - unimplemented!("Multiple profiles are currently unsupported!") - } - - let api_addrs = match profiles[0] { - Profile::Test => "127.0.0.1:0", - Profile::Default => "127.0.0.1:4004", - }; - let config_contents = CompatibleConfigFile { identity: Identity { peer_id, diff --git a/http/src/main.rs b/http/src/main.rs index ba1d6a705..e8ade71aa 100644 --- a/http/src/main.rs +++ b/http/src/main.rs @@ -19,7 +19,7 @@ enum Options { /// List of configuration profiles to apply. Currently only the `Test` and `Default` /// profiles are supported. /// - /// `Test` uses ephemeral ports, `Default` uses `4004`. + /// `Test` uses ephemeral ports (necessary for conformance tests), `Default` uses `4004`. #[structopt(long, use_delimiter = true)] profile: Vec, }, @@ -157,11 +157,6 @@ fn main() { let api_link_file = home.join("api"); - // If the port is specified, use that to start the server, if it isn't use ephemeral ports, - // i.e.: - // - // port => port - // none => 0 let (addr, server) = serve(&ipfs, api_listening_addrs); // shutdown future will handle signalling the exit From 4b58f0cdf77c1c6d5a277da8ccf26f6b8fe9da9f Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Fri, 23 Oct 2020 14:45:27 +0200 Subject: [PATCH 4/9] doc: changelog entry for #421 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45c61a98d..7a890a349 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Next +* fix(http): create `Profile` abstraction [#421] + +[#421]: https://github.com/rs-ipfs/rust-ipfs/pull/421 + # 0.2.1 * fix: restore_bootstrappers doesn't enable content discovery [#406] From b10efa41e069e4078842465496b6459c5caed484 Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Mon, 26 Oct 2020 09:57:54 +0100 Subject: [PATCH 5/9] fix(http): serialise addresses.api as API --- http/src/config.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/http/src/config.rs b/http/src/config.rs index 4ba358139..4216cedfd 100644 --- a/http/src/config.rs +++ b/http/src/config.rs @@ -64,10 +64,6 @@ pub fn initialize( // This check is done here to avoid an empty config file being created in the case of an // unsupported input. if profiles.len() != 1 { - // profiles are expected to be (comma separated) "test" as there are no bootstrap peer - // handling yet. the conformance test cases seem to init `go-ipfs` in this profile where - // it does not have any bootstrap nodes, and multi node tests later call swarm apis to - // dial the nodes together. unimplemented!("Multiple profiles are currently unsupported!"); } @@ -275,6 +271,7 @@ struct CompatibleConfigFile { #[serde(rename_all = "PascalCase")] struct Addresses { swarm: Vec, + #[serde(rename = "API")] api: SocketAddr, } From dfcfe84ef645de180b75a0da8096b033e21c25db Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Mon, 26 Oct 2020 10:11:36 +0100 Subject: [PATCH 6/9] fix(http): use singular for api listening addr --- http/src/config.rs | 4 ++-- http/src/main.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/http/src/config.rs b/http/src/config.rs index 4216cedfd..0fdb20647 100644 --- a/http/src/config.rs +++ b/http/src/config.rs @@ -86,7 +86,7 @@ fn create( use prost::Message; use std::io::BufWriter; - let api_addrs = match profiles[0] { + let api_addr = match profiles[0] { Profile::Test => "127.0.0.1:0", Profile::Default => "127.0.0.1:4004", }; @@ -143,7 +143,7 @@ fn create( }, addresses: Addresses { swarm: vec!["/ip4/127.0.0.1/tcp/0".parse().unwrap()], - api: api_addrs.parse().unwrap(), + api: api_addr.parse().unwrap(), }, }; diff --git a/http/src/main.rs b/http/src/main.rs index e8ade71aa..adb2db6a9 100644 --- a/http/src/main.rs +++ b/http/src/main.rs @@ -62,7 +62,7 @@ fn main() { let config_path = home.join("config"); - let (keypair, listening_addrs, api_listening_addrs) = match opts { + let (keypair, listening_addrs, api_listening_addr) = match opts { Options::Init { bits, profile } => { println!("initializing IPFS node at {:?}", home); @@ -157,7 +157,7 @@ fn main() { let api_link_file = home.join("api"); - let (addr, server) = serve(&ipfs, api_listening_addrs); + let (addr, server) = serve(&ipfs, api_listening_addr); // shutdown future will handle signalling the exit drop(ipfs); @@ -189,7 +189,7 @@ fn main() { fn serve( ipfs: &Ipfs, - listening_addrs: std::net::SocketAddr, + listening_addr: std::net::SocketAddr, ) -> (std::net::SocketAddr, impl std::future::Future) { use tokio::stream::StreamExt; use warp::Filter; @@ -200,7 +200,7 @@ fn serve( let ipfs = ipfs.clone(); - warp::serve(routes).bind_with_graceful_shutdown(listening_addrs, async move { + warp::serve(routes).bind_with_graceful_shutdown(listening_addr, async move { shutdown_rx.next().await; info!("Shutdown trigger received; starting shutdown"); ipfs.exit_daemon().await; From 2039bc0ba9a468c317afae8de4ec010af0581784 Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Mon, 26 Oct 2020 10:12:18 +0100 Subject: [PATCH 7/9] doc: fix changelog entry for #421 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a890a349..249b82013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Next -* fix(http): create `Profile` abstraction [#421] +* feat(http): create `Profile` abstraction [#421] [#421]: https://github.com/rs-ipfs/rust-ipfs/pull/421 From 9fb93fc63d72cd5a93958cd7052bb5d6fe429a75 Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Mon, 26 Oct 2020 12:44:08 +0100 Subject: [PATCH 8/9] fix(http): use multiaddr instead of socketaddr in config --- http/src/config.rs | 13 ++++++------- http/src/main.rs | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/http/src/config.rs b/http/src/config.rs index 0fdb20647..ef66800c2 100644 --- a/http/src/config.rs +++ b/http/src/config.rs @@ -1,9 +1,8 @@ //! go-ipfs compatible configuration file handling and setup. -use parity_multiaddr::Multiaddr; +use parity_multiaddr::{multiaddr, Multiaddr}; use serde::{Deserialize, Serialize}; use std::fs::{self, File}; -use std::net::SocketAddr; use std::num::NonZeroU16; use std::path::Path; use std::str::FromStr; @@ -87,8 +86,8 @@ fn create( use std::io::BufWriter; let api_addr = match profiles[0] { - Profile::Test => "127.0.0.1:0", - Profile::Default => "127.0.0.1:4004", + Profile::Test => multiaddr!(Ip4([127, 0, 0, 1]), Tcp(0u16)), + Profile::Default => multiaddr!(Ip4([127, 0, 0, 1]), Tcp(4004u16)), }; let bits = bits.get(); @@ -143,7 +142,7 @@ fn create( }, addresses: Addresses { swarm: vec!["/ip4/127.0.0.1/tcp/0".parse().unwrap()], - api: api_addr.parse().unwrap(), + api: api_addr, }, }; @@ -173,7 +172,7 @@ pub enum LoadingError { /// Returns only the keypair and listening addresses or [`LoadingError`] but this should be /// extended to contain the bootstrap nodes at least later when we need to support those for /// testing purposes. -pub fn load(config: File) -> Result<(ipfs::Keypair, Vec, SocketAddr), LoadingError> { +pub fn load(config: File) -> Result<(ipfs::Keypair, Vec, Multiaddr), LoadingError> { use std::io::BufReader; let CompatibleConfigFile { @@ -272,7 +271,7 @@ struct CompatibleConfigFile { struct Addresses { swarm: Vec, #[serde(rename = "API")] - api: SocketAddr, + api: Multiaddr, } #[derive(Debug, Serialize, Deserialize)] diff --git a/http/src/main.rs b/http/src/main.rs index adb2db6a9..2a3024b36 100644 --- a/http/src/main.rs +++ b/http/src/main.rs @@ -4,6 +4,7 @@ use structopt::StructOpt; use ipfs::{Ipfs, IpfsOptions, IpfsTypes, UninitializedIpfs}; use ipfs_http::{config, v0}; +use parity_multiaddr::{Multiaddr, Protocol}; #[macro_use] extern crate tracing; @@ -189,10 +190,11 @@ fn main() { fn serve( ipfs: &Ipfs, - listening_addr: std::net::SocketAddr, + listening_addr: Multiaddr, ) -> (std::net::SocketAddr, impl std::future::Future) { use tokio::stream::StreamExt; use warp::Filter; + let (shutdown_tx, mut shutdown_rx) = tokio::sync::mpsc::channel::<()>(1); let routes = v0::routes(ipfs, shutdown_tx); @@ -200,7 +202,17 @@ fn serve( let ipfs = ipfs.clone(); - warp::serve(routes).bind_with_graceful_shutdown(listening_addr, async move { + let components = listening_addr.iter().collect::>(); + + use std::net::SocketAddr; + let socket_addr = + if let (Protocol::Ip4(ip), Protocol::Tcp(port)) = (&components[0], &components[1]) { + SocketAddr::new(ip.clone().into(), *port) + } else { + panic!("Couldn't convert MultiAddr into SocketAddr") + }; + + warp::serve(routes).bind_with_graceful_shutdown(socket_addr, async move { shutdown_rx.next().await; info!("Shutdown trigger received; starting shutdown"); ipfs.exit_daemon().await; From 3d677c6ecc02fdbf56df5ecb943c532e298c572b Mon Sep 17 00:00:00 2001 From: Niklas Long Date: Tue, 27 Oct 2020 14:47:40 +0100 Subject: [PATCH 9/9] fix(http): use slice pattern matching for MultiAddr match/conversion to SocketAddr --- http/src/main.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/http/src/main.rs b/http/src/main.rs index 2a3024b36..82e4de6ff 100644 --- a/http/src/main.rs +++ b/http/src/main.rs @@ -192,6 +192,7 @@ fn serve( ipfs: &Ipfs, listening_addr: Multiaddr, ) -> (std::net::SocketAddr, impl std::future::Future) { + use std::net::SocketAddr; use tokio::stream::StreamExt; use warp::Filter; @@ -204,13 +205,13 @@ fn serve( let components = listening_addr.iter().collect::>(); - use std::net::SocketAddr; - let socket_addr = - if let (Protocol::Ip4(ip), Protocol::Tcp(port)) = (&components[0], &components[1]) { - SocketAddr::new(ip.clone().into(), *port) - } else { - panic!("Couldn't convert MultiAddr into SocketAddr") - }; + let socket_addr = match components.as_slice() { + [Protocol::Ip4(ip), Protocol::Tcp(port)] => SocketAddr::new(ip.clone().into(), *port), + _ => panic!( + "Couldn't convert MultiAddr into SocketAddr: {}", + listening_addr + ), + }; warp::serve(routes).bind_with_graceful_shutdown(socket_addr, async move { shutdown_rx.next().await;