Skip to content
This repository was archived by the owner on Oct 23, 2022. It is now read-only.

Commit 83e73ab

Browse files
Merge #421
421: feat(http): create Profile abstraction to allow port choice r=koivunej a=niklaslong This PR introduces a `Profile` abstraction to allow the customisation of port selection as discussed in #402. Two profiles are supported: - `Test` for use with conformance tests (ephemeral port selection) - `Default` serves on `4004` Co-authored-by: Niklas Long <[email protected]>
2 parents e3e1cc2 + 3d677c6 commit 83e73ab

File tree

3 files changed

+71
-22
lines changed

3 files changed

+71
-22
lines changed

Diff for: CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Next
22

3+
* feat(http): create `Profile` abstraction [#421]
4+
5+
[#421]: https://github.com/rs-ipfs/rust-ipfs/pull/421
6+
37
# 0.2.1
48

59
* fix: restore_bootstrappers doesn't enable content discovery [#406]

Diff for: http/src/config.rs

+41-14
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//! go-ipfs compatible configuration file handling and setup.
22
3-
use parity_multiaddr::Multiaddr;
3+
use parity_multiaddr::{multiaddr, Multiaddr};
44
use serde::{Deserialize, Serialize};
55
use std::fs::{self, File};
66
use std::num::NonZeroU16;
77
use std::path::Path;
8+
use std::str::FromStr;
9+
use structopt::StructOpt;
810
use thiserror::Error;
911

1012
/// Temporary module required to de/ser config files base64'd protobuf rsa private key format.
@@ -13,6 +15,25 @@ mod keys_proto {
1315
include!(concat!(env!("OUT_DIR"), "/keys_proto.rs"));
1416
}
1517

18+
#[derive(Debug, StructOpt)]
19+
pub enum Profile {
20+
Test,
21+
Default,
22+
}
23+
24+
// Required for structopt.
25+
impl FromStr for Profile {
26+
type Err = InitializationError;
27+
28+
fn from_str(profile: &str) -> Result<Self, Self::Err> {
29+
match profile {
30+
"test" => Ok(Profile::Test),
31+
"default" => Ok(Profile::Default),
32+
_ => Err(InitializationError::InvalidProfile(profile.to_string())),
33+
}
34+
}
35+
}
36+
1637
/// The way things can go wrong when calling [`initialize`].
1738
#[derive(Error, Debug)]
1839
pub enum InitializationError {
@@ -23,7 +44,7 @@ pub enum InitializationError {
2344
#[error("invalid RSA key length given: {0}")]
2445
InvalidRsaKeyLength(u16),
2546
#[error("unsupported profiles selected: {0:?}")]
26-
InvalidProfiles(Vec<String>),
47+
InvalidProfile(String),
2748
#[error("key generation failed: {0}")]
2849
KeyGeneration(Box<dyn std::error::Error + 'static>),
2950
#[error("key encoding failed: {0}")]
@@ -37,8 +58,14 @@ pub enum InitializationError {
3758
pub fn initialize(
3859
ipfs_path: &Path,
3960
bits: NonZeroU16,
40-
profiles: Vec<String>,
61+
profiles: Vec<Profile>,
4162
) -> Result<(), InitializationError> {
63+
// This check is done here to avoid an empty config file being created in the case of an
64+
// unsupported input.
65+
if profiles.len() != 1 {
66+
unimplemented!("Multiple profiles are currently unsupported!");
67+
}
68+
4269
let config_path = ipfs_path.join("config");
4370

4471
fs::create_dir_all(&ipfs_path)
@@ -52,27 +79,24 @@ pub fn initialize(
5279
fn create(
5380
config: File,
5481
bits: NonZeroU16,
55-
profiles: Vec<String>,
82+
profiles: Vec<Profile>,
5683
) -> Result<(), InitializationError> {
5784
use multibase::Base::Base64Pad;
5885
use prost::Message;
5986
use std::io::BufWriter;
6087

88+
let api_addr = match profiles[0] {
89+
Profile::Test => multiaddr!(Ip4([127, 0, 0, 1]), Tcp(0u16)),
90+
Profile::Default => multiaddr!(Ip4([127, 0, 0, 1]), Tcp(4004u16)),
91+
};
92+
6193
let bits = bits.get();
6294

6395
if bits < 2048 || bits > 16 * 1024 {
6496
// ring will not accept a less than 2048 key
6597
return Err(InitializationError::InvalidRsaKeyLength(bits));
6698
}
6799

68-
if profiles.len() != 1 || profiles[0] != "test" {
69-
// profiles are expected to be (comma separated) "test" as there are no bootstrap peer
70-
// handling yet. the conformance test cases seem to init `go-ipfs` in this profile where
71-
// it does not have any bootstrap nodes, and multi node tests later call swarm apis to
72-
// dial the nodes together.
73-
return Err(InitializationError::InvalidProfiles(profiles));
74-
}
75-
76100
let pk = openssl::rsa::Rsa::generate(bits as u32)
77101
.map_err(|e| InitializationError::KeyGeneration(Box::new(e)))?;
78102

@@ -118,6 +142,7 @@ fn create(
118142
},
119143
addresses: Addresses {
120144
swarm: vec!["/ip4/127.0.0.1/tcp/0".parse().unwrap()],
145+
api: api_addr,
121146
},
122147
};
123148

@@ -147,7 +172,7 @@ pub enum LoadingError {
147172
/// Returns only the keypair and listening addresses or [`LoadingError`] but this should be
148173
/// extended to contain the bootstrap nodes at least later when we need to support those for
149174
/// testing purposes.
150-
pub fn load(config: File) -> Result<(ipfs::Keypair, Vec<Multiaddr>), LoadingError> {
175+
pub fn load(config: File) -> Result<(ipfs::Keypair, Vec<Multiaddr>, Multiaddr), LoadingError> {
151176
use std::io::BufReader;
152177

153178
let CompatibleConfigFile {
@@ -167,7 +192,7 @@ pub fn load(config: File) -> Result<(ipfs::Keypair, Vec<Multiaddr>), LoadingErro
167192
});
168193
}
169194

170-
Ok((kp, addresses.swarm))
195+
Ok((kp, addresses.swarm, addresses.api))
171196
}
172197

173198
/// Converts a PEM format to DER where PEM is a container for Base64 data with padding, starting on
@@ -245,6 +270,8 @@ struct CompatibleConfigFile {
245270
#[serde(rename_all = "PascalCase")]
246271
struct Addresses {
247272
swarm: Vec<Multiaddr>,
273+
#[serde(rename = "API")]
274+
api: Multiaddr,
248275
}
249276

250277
#[derive(Debug, Serialize, Deserialize)]

Diff for: http/src/main.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use structopt::StructOpt;
44

55
use ipfs::{Ipfs, IpfsOptions, IpfsTypes, UninitializedIpfs};
66
use ipfs_http::{config, v0};
7+
use parity_multiaddr::{Multiaddr, Protocol};
78

89
#[macro_use]
910
extern crate tracing;
@@ -16,9 +17,12 @@ enum Options {
1617
/// Generated key length
1718
#[structopt(long)]
1819
bits: NonZeroU16,
19-
/// List of configuration profiles to apply
20+
/// List of configuration profiles to apply. Currently only the `Test` and `Default`
21+
/// profiles are supported.
22+
///
23+
/// `Test` uses ephemeral ports (necessary for conformance tests), `Default` uses `4004`.
2024
#[structopt(long, use_delimiter = true)]
21-
profile: Vec<String>,
25+
profile: Vec<config::Profile>,
2226
},
2327
/// Start the IPFS node in the foreground (not detaching from parent process).
2428
Daemon,
@@ -59,7 +63,7 @@ fn main() {
5963

6064
let config_path = home.join("config");
6165

62-
let (keypair, listening_addrs) = match opts {
66+
let (keypair, listening_addrs, api_listening_addr) = match opts {
6367
Options::Init { bits, profile } => {
6468
println!("initializing IPFS node at {:?}", home);
6569

@@ -73,7 +77,7 @@ fn main() {
7377

7478
match result {
7579
Ok(_) => {
76-
let (kp, _) = std::fs::File::open(config_path)
80+
let (kp, _, _) = std::fs::File::open(config_path)
7781
.map_err(config::LoadingError::ConfigurationFileOpening)
7882
.and_then(config::load)
7983
.unwrap();
@@ -101,8 +105,8 @@ fn main() {
101105
eprintln!("This is a fake version of ipfs cli which does not support much");
102106
std::process::exit(1);
103107
}
104-
Err(config::InitializationError::InvalidProfiles(profiles)) => {
105-
eprintln!("Error: unsupported profile selection: {:?}", profiles);
108+
Err(config::InitializationError::InvalidProfile(profile)) => {
109+
eprintln!("Error: unsupported profile selection: {:?}", profile);
106110
eprintln!("This is a fake version of ipfs cli which does not support much");
107111
std::process::exit(1);
108112
}
@@ -153,7 +157,8 @@ fn main() {
153157
tokio::spawn(task);
154158

155159
let api_link_file = home.join("api");
156-
let (addr, server) = serve(&ipfs);
160+
161+
let (addr, server) = serve(&ipfs, api_listening_addr);
157162

158163
// shutdown future will handle signalling the exit
159164
drop(ipfs);
@@ -185,17 +190,30 @@ fn main() {
185190

186191
fn serve<Types: IpfsTypes>(
187192
ipfs: &Ipfs<Types>,
193+
listening_addr: Multiaddr,
188194
) -> (std::net::SocketAddr, impl std::future::Future<Output = ()>) {
195+
use std::net::SocketAddr;
189196
use tokio::stream::StreamExt;
190197
use warp::Filter;
198+
191199
let (shutdown_tx, mut shutdown_rx) = tokio::sync::mpsc::channel::<()>(1);
192200

193201
let routes = v0::routes(ipfs, shutdown_tx);
194202
let routes = routes.with(warp::log(env!("CARGO_PKG_NAME")));
195203

196204
let ipfs = ipfs.clone();
197205

198-
warp::serve(routes).bind_with_graceful_shutdown(([127, 0, 0, 1], 0), async move {
206+
let components = listening_addr.iter().collect::<Vec<_>>();
207+
208+
let socket_addr = match components.as_slice() {
209+
[Protocol::Ip4(ip), Protocol::Tcp(port)] => SocketAddr::new(ip.clone().into(), *port),
210+
_ => panic!(
211+
"Couldn't convert MultiAddr into SocketAddr: {}",
212+
listening_addr
213+
),
214+
};
215+
216+
warp::serve(routes).bind_with_graceful_shutdown(socket_addr, async move {
199217
shutdown_rx.next().await;
200218
info!("Shutdown trigger received; starting shutdown");
201219
ipfs.exit_daemon().await;

0 commit comments

Comments
 (0)