From b63ce8c1120de987a0252fe3128e20eab6aa363f Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Thu, 28 Apr 2022 17:34:22 +0800 Subject: [PATCH 01/14] added send_file example --- src/transfer.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/transfer.rs b/src/transfer.rs index d02c0515..5e69075b 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -253,6 +253,45 @@ where /// /// You must ensure that the Reader contains exactly as many bytes /// as advertized in file_size. +/// +/// # Example +/// +/// ```no_run +/// # use magic_wormhole::{Wormhole, WormholeError}; +/// use magic_wormhole::transfer::{APP_CONFIG, send_file}; +/// use magic_wormhole::transit; +/// use async_std::fs::File; +/// +/// # #[async_std::main] +/// # async fn main() -> Result<(), Box> { +/// let filepath = "foobar.baz"; +/// let mut file = File::create(filepath).await.unwrap(); +/// let file_length = file.metadata().await.unwrap().len(); +/// +/// // Wormhole connection +/// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; +/// let connector = wormhole.await.unwrap(); +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse().unwrap(); +/// +/// // Transfer status +/// let mut bytes_sent = 0; +/// let mut bytes_total = 0; +/// let progress = move |sent, total| { +/// bytes_sent = sent; +/// bytes_total = total; +/// }; +/// +/// let cancel = futures::future::pending(); +/// +/// send_file( +/// connector, relay_url, +/// &mut file, filepath, file_length, +/// transit::Abilities::ALL_ABILITIES, progress, cancel, +/// ).await; +/// +/// # std::fs::remove_file(filepath).unwrap(); //cleanup +/// # Ok(()) +/// # } pub async fn send_file( wormhole: Wormhole, relay_url: url::Url, From 1b26da19df92b1946f8b429d31e332ea6ac2b4e4 Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Thu, 28 Apr 2022 17:35:09 +0800 Subject: [PATCH 02/14] added wormhole connection examples --- src/core.rs | 53 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/core.rs b/src/core.rs index 8a2c695b..d2069641 100644 --- a/src/core.rs +++ b/src/core.rs @@ -117,15 +117,28 @@ pub struct Wormhole { } impl Wormhole { - /** - * Generate a code and connect to the rendezvous server. - * - * # Returns - * - * A tuple with a [`WormholeWelcome`] and a [`std::future::Future`] that will - * do the rest of the client-client handshake and yield the [`Wormhole`] object - * on success. - */ + /// Generate a code and connect to the rendezvous server, `code_length` is the + /// number of words after the nameplate + /// + /// # Returns + /// + /// A tuple with a [`WormholeWelcome`] and a [`std::future::Future`] that will + /// do the rest of the client-client handshake and yield the [`Wormhole`] object + /// on success. + /// + /// # Example + /// ``` + /// # #[async_std::main] + /// # async fn main() -> Result<(), magic_wormhole::WormholeError> { + /// # use magic_wormhole::Wormhole; + /// use magic_wormhole::transfer::APP_CONFIG; + /// + /// let num_words = 2; + /// let (welcome, wormhole) = Wormhole::connect_without_code(APP_CONFIG, num_words).await?; + /// let wormhole_code = welcome.code.0; + /// # Ok(()) + /// # } + /// ``` pub async fn connect_without_code( config: AppConfig, code_length: usize, @@ -160,9 +173,25 @@ impl Wormhole { )) } - /** - * Connect to a peer with a code. - */ + /// Connect to a peer with a code. + /// + /// # Returns + /// + /// A [`WormholeWelcome`] and a [`std::future::Future`] which yields + /// a Wormhole object + /// + /// # Example + /// ```no_run + /// # #[async_std::main] + /// # async fn main() -> Result<(), magic_wormhole::WormholeError> { + /// # use magic_wormhole::Wormhole; + /// use magic_wormhole::{Code, transfer::APP_CONFIG}; + /// + /// let code = Code("1-aardvark-Zulu".to_string()); + /// let (welcome, wormhole) = Wormhole::connect_with_code(APP_CONFIG, code).await?; + /// # Ok(()) + /// # } + /// ``` pub async fn connect_with_code( config: AppConfig, code: Code, From 44ec12699f37e0bd9d9933d51dcc46496189c3c3 Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Thu, 28 Apr 2022 17:40:10 +0800 Subject: [PATCH 03/14] needed a cargo update for cli-clipboard to build --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 1241614b..11e73c48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -412,7 +412,7 @@ dependencies = [ [[package]] name = "cli-clipboard" version = "0.2.1" -source = "git+https://github.com/piegamesde/cli-clipboard#8447bd4ac00af578d21ff440752e7aa837c07f30" +source = "git+https://github.com/piegamesde/cli-clipboard#87ed1bdf6c6cc7f66ef2a161381c49345cec60b5" dependencies = [ "clipboard-win", "objc", From b3eed631613cb9c43018a22c13744aa7144d0e41 Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Tue, 3 May 2022 15:06:21 +0800 Subject: [PATCH 04/14] added doc examples --- src/transfer.rs | 160 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 141 insertions(+), 19 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 5e69075b..d42cd985 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -200,7 +200,43 @@ impl TransitAck { serde_json::to_vec(self).unwrap() } } - +/// Sends either a file or folder to the other side +/// +/// # Example +/// +/// ```no_run +/// # use magic_wormhole::{Wormhole, WormholeError}; +/// use magic_wormhole::transfer::{send_file_or_folder, APP_CONFIG}; +/// use magic_wormhole::transit; +/// +/// # #[async_std::main] +/// # async fn main() -> Result<(), Box> { +/// let folder_path = "./foo/bar"; +/// let folder_name = "bar"; +/// +/// // Wormhole connection +/// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; +/// let connector = wormhole.await?; +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; +/// +/// // Transfer status +/// let mut bytes_sent = 0; +/// let mut bytes_total = 0; +/// let progress = move |sent, total| { +/// bytes_sent = sent; +/// bytes_total = total; +/// }; +/// +/// let cancel = futures::future::pending(); +/// let abilities = transit::Abilities::ALL_ABILITIES; +/// +/// send_file_or_folder( +/// connector, relay_url, +/// folder_path, folder_name, +/// abilities, progress, cancel +/// ).await?; +/// # Ok(()) +/// # } pub async fn send_file_or_folder( wormhole: Wormhole, relay_url: url::Url, @@ -258,20 +294,20 @@ where /// /// ```no_run /// # use magic_wormhole::{Wormhole, WormholeError}; -/// use magic_wormhole::transfer::{APP_CONFIG, send_file}; +/// use magic_wormhole::transfer::{send_file, APP_CONFIG}; /// use magic_wormhole::transit; /// use async_std::fs::File; /// /// # #[async_std::main] /// # async fn main() -> Result<(), Box> { -/// let filepath = "foobar.baz"; -/// let mut file = File::create(filepath).await.unwrap(); -/// let file_length = file.metadata().await.unwrap().len(); +/// let filepath = "foo.txt"; +/// let mut file = File::create(filepath).await?; +/// let file_length = file.metadata().await?.len(); /// /// // Wormhole connection /// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; -/// let connector = wormhole.await.unwrap(); -/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse().unwrap(); +/// let connector = wormhole.await?; +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; /// /// // Transfer status /// let mut bytes_sent = 0; @@ -282,14 +318,13 @@ where /// }; /// /// let cancel = futures::future::pending(); +/// let abilities = transit::Abilities::ALL_ABILITIES; /// /// send_file( /// connector, relay_url, /// &mut file, filepath, file_length, -/// transit::Abilities::ALL_ABILITIES, progress, cancel, -/// ).await; -/// -/// # std::fs::remove_file(filepath).unwrap(); //cleanup +/// abilities, progress, cancel +/// ).await?; /// # Ok(()) /// # } pub async fn send_file( @@ -332,6 +367,43 @@ where /// This isn't a proper folder transfer as per the Wormhole protocol /// because it sends it in a way so that the receiver still has to manually /// unpack it. But it's better than nothing +/// +/// # Example +/// +/// ```no_run +/// # use magic_wormhole::{Wormhole, WormholeError}; +/// use magic_wormhole::transfer::{send_folder, APP_CONFIG}; +/// use magic_wormhole::transit; +/// +/// # #[async_std::main] +/// # async fn main() -> Result<(), Box> { +/// let folder_path = "./foo/bar"; +/// let folder_name = "bar"; +/// +/// // Wormhole connection +/// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; +/// let connector = wormhole.await?; +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; +/// +/// // Transfer status +/// let mut bytes_sent = 0; +/// let mut bytes_total = 0; +/// let progress = move |sent, total| { +/// bytes_sent = sent; +/// bytes_total = total; +/// }; +/// +/// let cancel = futures::future::pending(); +/// let abilities = transit::Abilities::ALL_ABILITIES; +/// +/// send_folder( +/// connector, relay_url, +/// folder_path, folder_name, +/// abilities, progress, cancel +/// ).await?; +/// +/// # Ok(()) +/// # } pub async fn send_folder( wormhole: Wormhole, relay_url: url::Url, @@ -359,14 +431,32 @@ where .await } -/** - * Wait for a file offer from the other side - * - * This method waits for an offer message and builds up a [`ReceiveRequest`](ReceiveRequest). - * It will also start building a TCP connection to the other side using the transit protocol. - * - * Returns `None` if the task got cancelled. - */ +/// Wait for a file offer from the other side +/// +/// This method waits for an offer message and builds up a [`ReceiveRequest`](ReceiveRequest). +/// It will also start building a TCP connection to the other side using the transit protocol. +/// +/// Returns `None` if the task got cancelled. +/// +/// # Example +/// +/// ```no_run +/// # use magic_wormhole::{Wormhole, WormholeError}; +/// use magic_wormhole::transfer::{request_file, APP_CONFIG}; +/// use magic_wormhole::transit; +/// +/// # #[async_std::main] +/// # async fn main() -> Result<(), Box> { +/// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; +/// let mut connector = wormhole.await?; +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; +/// +/// let abilities = transit::Abilities::ALL_ABILITIES; +/// let cancel = futures::future::pending(); +/// +/// let mut receive_request = request_file(connector, relay_url, abilities, cancel).await?; +/// # Ok(()) +/// # } pub async fn request_file( mut wormhole: Wormhole, relay_url: url::Url, @@ -458,6 +548,8 @@ pub async fn request_file( * A pending files send offer from the other side * * You *should* consume this object, either by calling [`accept`](ReceiveRequest::accept) or [`reject`](ReceiveRequest::reject). + * + * Constructed from the [`request_file`](self::request_file) function. */ #[must_use] pub struct ReceiveRequest { @@ -476,6 +568,36 @@ impl ReceiveRequest { * * This will transfer the file and save it on disk. */ + /// # Example + /// + /// ```no_run + /// # use magic_wormhole::{Wormhole, WormholeError}; + /// # use magic_wormhole::transfer::{request_file, APP_CONFIG}; + /// # use magic_wormhole::transit; + /// use async_std::fs::File; + /// + /// # #[async_std::main] + /// # async fn main() -> Result<(), Box> { + /// # let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; + /// # let mut wormhole = wormhole.await?; + /// # let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; + /// # let abilities = transit::Abilities::ALL_ABILITIES; + /// # let cancel = futures::future::pending(); + /// let mut receive_request = request_file(wormhole, relay_url, abilities, cancel).await?; + /// let mut file = File::create("foo.txt").await?; + /// + /// let mut bytes_sent = 0; + /// let mut bytes_total = 0; + /// let progress = move |sent, total| { + /// bytes_sent = sent; + /// bytes_total = total; + /// }; + /// let cancel = futures::future::pending(); + /// + /// receive_request.unwrap().accept(progress, &mut file, cancel); + /// + /// # Ok(()) + /// # } pub async fn accept( mut self, progress_handler: F, From 0cfbeee35fc643cd90d0f028d11477a258ea1e5d Mon Sep 17 00:00:00 2001 From: piegames Date: Thu, 24 Mar 2022 15:15:34 +0100 Subject: [PATCH 05/14] Fix relay signalling --- changelog.md | 3 +++ src/transfer/messages.rs | 10 ++------ src/transit.rs | 54 ++++++---------------------------------- 3 files changed, 13 insertions(+), 54 deletions(-) diff --git a/changelog.md b/changelog.md index 8e3494dc..29863260 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,9 @@ ## Unreleased +- \[lib\]\[breaking\] Removed `relay-v2` ability again. + - This fixed some relay signalling issues, where no connection could be made with `--force-relay` under some circumstances. + ## Version 0.4.0 - When sending, the code will now aumatically be copied into clipboard. So you don't have to select it in the terminal anymore before pasting! diff --git a/src/transfer/messages.rs b/src/transfer/messages.rs index caf90797..33f79ecd 100644 --- a/src/transfer/messages.rs +++ b/src/transfer/messages.rs @@ -189,19 +189,13 @@ mod test { serde_json::json!(crate::transfer::PeerMessage::transit(abilities, hints)), serde_json::json!({ "transit": { - "abilities-v1": [{"type":"direct-tcp-v1"},{"type":"relay-v1"},{"type":"relay-v2"}], + "abilities-v1": [{"type":"direct-tcp-v1"},{"type":"relay-v1"}], "hints-v1": [ {"hostname":"192.168.1.8","port":46295,"type":"direct-tcp-v1"}, { "type": "relay-v1", "hints": [ - {"hostname": "magic-wormhole-transit.debian.net", "port": 4001 } - ] - }, - { - "type": "relay-v2", - "hints": [ - {"type": "tcp", "hostname": "magic-wormhole-transit.debian.net", "port": 4001} + {"type": "direct-tcp-v1", "hostname": "magic-wormhole-transit.debian.net", "port": 4001} ], "name": null } diff --git a/src/transit.rs b/src/transit.rs index ca76ef50..7636d883 100644 --- a/src/transit.rs +++ b/src/transit.rs @@ -107,17 +107,14 @@ pub enum TransitError { pub struct Abilities { /** Direct connection to the peer */ pub direct_tcp_v1: bool, - /** Connection over a TCP relay */ + /** Connection over a relay */ pub relay_v1: bool, - /** Connection over a TCP or WebSocket relay */ - pub relay_v2: bool, } impl Abilities { pub const ALL_ABILITIES: Self = Self { direct_tcp_v1: true, relay_v1: true, - relay_v2: true, }; /** @@ -129,7 +126,6 @@ impl Abilities { pub const FORCE_DIRECT: Self = Self { direct_tcp_v1: true, relay_v1: false, - relay_v2: false, }; /** @@ -143,7 +139,6 @@ impl Abilities { pub const FORCE_RELAY: Self = Self { direct_tcp_v1: false, relay_v1: true, - relay_v2: true, }; pub fn can_direct(&self) -> bool { @@ -151,14 +146,13 @@ impl Abilities { } pub fn can_relay(&self) -> bool { - self.relay_v1 || self.relay_v2 + self.relay_v1 } /** Keep only abilities that both sides support */ pub fn intersect(mut self, other: &Self) -> Self { self.direct_tcp_v1 &= other.direct_tcp_v1; self.relay_v1 &= other.relay_v1; - self.relay_v2 &= other.relay_v2; self } } @@ -179,11 +173,6 @@ impl serde::Serialize for Abilities { "type": "relay-v1", })); } - if self.relay_v2 { - hints.push(serde_json::json!({ - "type": "relay-v2", - })); - } serde_json::Value::Array(hints).serialize(ser) } } @@ -213,9 +202,6 @@ impl<'de> serde::Deserialize<'de> for Abilities { Ability::RelayV1 => { abilities.relay_v1 = true; }, - Ability::RelayV2 => { - abilities.relay_v2 = true; - }, _ => (), } } @@ -229,10 +215,7 @@ impl<'de> serde::Deserialize<'de> for Abilities { #[non_exhaustive] enum HintSerde { DirectTcpV1(DirectHint), - RelayV1 { - hints: HashSet, - }, - RelayV2(RelayHint), + RelayV1(RelayHint), #[serde(other)] Unknown, } @@ -273,13 +256,7 @@ impl<'de> serde::Deserialize<'de> for Hints { HintSerde::DirectTcpV1(hint) => { direct_tcp.insert(hint); }, - HintSerde::RelayV1 { hints } => { - relay.push(RelayHint { - tcp: hints, - ..RelayHint::default() - }); - }, - HintSerde::RelayV2(hint) => { + HintSerde::RelayV1(hint) => { relay_v2.push(hint); }, /* Ignore unknown hints */ @@ -303,14 +280,7 @@ impl serde::Serialize for Hints { S: serde::Serializer, { let direct = self.direct_tcp.iter().cloned().map(HintSerde::DirectTcpV1); - let relay = self.relay.iter().flat_map(|hint| { - [ - HintSerde::RelayV1 { - hints: hint.tcp.clone(), - }, - HintSerde::RelayV2(hint.clone()), - ] - }); + let relay = self.relay.iter().cloned().map(HintSerde::RelayV1); ser.collect_seq(direct.chain(relay)) } } @@ -350,6 +320,7 @@ struct RelayHintSerde { #[serde(rename_all = "kebab-case", tag = "type")] #[non_exhaustive] enum RelayHintSerdeInner { + #[serde(rename = "direct-tcp-v1")] Tcp(DirectHint), Websocket { url: url::Url, @@ -1507,7 +1478,7 @@ mod test { pub fn test_abilities_encoding() { assert_eq!( serde_json::to_value(Abilities::ALL_ABILITIES).unwrap(), - json!([{"type": "direct-tcp-v1"}, {"type": "relay-v1"}, {"type": "relay-v2"}]) + json!([{"type": "direct-tcp-v1"}, {"type": "relay-v1"}]) ); assert_eq!( serde_json::to_value(Abilities::FORCE_DIRECT).unwrap(), @@ -1538,19 +1509,10 @@ mod test { }, { "type": "relay-v1", - "hints": [ - { - "hostname": "transit.magic-wormhole.io", - "port": 4001, - } - ] - }, - { - "type": "relay-v2", "name": "default", "hints": [ { - "type": "tcp", + "type": "direct-tcp-v1", "hostname": "transit.magic-wormhole.io", "port": 4001, }, From 16061126144da00e037e81321e8624ed436c58d4 Mon Sep 17 00:00:00 2001 From: piegames Date: Fri, 29 Apr 2022 15:01:09 +0200 Subject: [PATCH 06/14] Update cli-clipboard dependency The upstream PR has been merged, but no release yet --- Cargo.lock | 2 +- cli/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1241614b..fa5cb0ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -412,7 +412,7 @@ dependencies = [ [[package]] name = "cli-clipboard" version = "0.2.1" -source = "git+https://github.com/piegamesde/cli-clipboard#8447bd4ac00af578d21ff440752e7aa837c07f30" +source = "git+https://github.com/ActuallyAllie/cli-clipboard#c376ba6ce17b71feeed1693f1e5267cf73d7d14b" dependencies = [ "clipboard-win", "objc", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index f46e4e71..7be879a4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -22,4 +22,4 @@ dialoguer = "0.10.0" color-eyre = "0.6.0" number_prefix = "0.4.0" ctrlc = "3.2.1" -cli-clipboard = { git = "https://github.com/piegamesde/cli-clipboard" } +cli-clipboard = { git = "https://github.com/ActuallyAllie/cli-clipboard" } From 701f26739f7b0047b945e29c15dc2c27465ae6c3 Mon Sep 17 00:00:00 2001 From: piegames Date: Thu, 24 Mar 2022 22:54:15 +0100 Subject: [PATCH 07/14] Transit: expose information about the established connection Closes #150 --- changelog.md | 3 ++ cli/src/main.rs | 29 +++++++++-- src/core/test.rs | 13 ++++- src/forwarding.rs | 8 ++- src/lib.rs | 1 + src/transfer.rs | 23 +++++++-- src/transfer/v1.rs | 14 +++-- src/transit.rs | 125 ++++++++++++++++++++++----------------------- 8 files changed, 133 insertions(+), 83 deletions(-) diff --git a/changelog.md b/changelog.md index 29863260..58210c33 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,9 @@ - \[lib\]\[breaking\] Removed `relay-v2` ability again. - This fixed some relay signalling issues, where no connection could be made with `--force-relay` under some circumstances. +- \[lib\]\[breaking\] Exposed the state of the established transit connection + - The `transit` module now returns whether the established connection is direct or not and the peer/relay's IP address + - The `transfer` and `forwarding` modules now take a `transit_handler` argument. Use `&transit::log_transit_connection` as default value ## Version 0.4.0 diff --git a/cli/src/main.rs b/cli/src/main.rs index b45d3d66..86d31503 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,3 +1,4 @@ +#![allow(clippy::too_many_arguments)] mod util; use std::{ @@ -501,6 +502,7 @@ async fn main() -> eyre::Result<()> { let relay_server = vec![transit::RelayHint::from_urls(None, [relay_server])]; async_std::task::spawn(forwarding::serve( wormhole, + &transit::log_transit_connection, relay_server, targets.clone(), ctrl_c(), @@ -532,8 +534,14 @@ async fn main() -> eyre::Result<()> { .await?; let relay_server = vec![transit::RelayHint::from_urls(None, [relay_server])]; - let offer = - forwarding::connect(wormhole, relay_server, Some(bind_address), &ports).await?; + let offer = forwarding::connect( + wormhole, + &transit::log_transit_connection, + relay_server, + Some(bind_address), + &ports, + ) + .await?; log::info!("Mapping the following open ports to targets:"); log::info!(" local port -> remote target (no address = localhost on remote)"); for (port, target) in &offer.mapping { @@ -700,6 +708,7 @@ async fn send( file_path, file_name, transit_abilities, + &transit::log_transit_connection, move |sent, total| { if sent == 0 { pb.reset_elapsed(); @@ -716,7 +725,6 @@ async fn send( Ok(()) } -#[allow(clippy::too_many_arguments)] async fn send_many( relay_server: url::Url, code: &magic_wormhole::Code, @@ -810,6 +818,7 @@ async fn send_many( file_path.deref(), file_name.deref(), transit_abilities, + &transit::log_transit_connection, move |_sent, _total| { // if sent == 0 { // pb2.reset_elapsed(); @@ -904,7 +913,12 @@ async fn receive( .await .context("Failed to create destination file")?; return req - .accept(on_progress, &mut file, ctrl_c()) + .accept( + &transit::log_transit_connection, + on_progress, + &mut file, + ctrl_c(), + ) .await .context("Receive process failed"); } @@ -926,7 +940,12 @@ async fn receive( .open(&file_path) .await?; Ok(req - .accept(on_progress, &mut file, ctrl_c()) + .accept( + &transit::log_transit_connection, + on_progress, + &mut file, + ctrl_c(), + ) .await .context("Receive process failed")?) } diff --git a/src/core/test.rs b/src/core/test.rs index 6c0a6d1b..cd4be71e 100644 --- a/src/core/test.rs +++ b/src/core/test.rs @@ -57,6 +57,7 @@ pub async fn test_file_rust2rust() -> eyre::Result<()> { .unwrap() .len(), magic_wormhole::transit::Abilities::ALL_ABILITIES, + &transit::log_transit_connection, |_sent, _total| {}, futures::future::pending(), ) @@ -85,6 +86,7 @@ pub async fn test_file_rust2rust() -> eyre::Result<()> { let mut buffer = Vec::::new(); req.accept( + &transit::log_transit_connection, |_received, _total| {}, &mut buffer, futures::future::pending(), @@ -211,6 +213,7 @@ pub async fn test_send_many() -> eyre::Result<()> { .unwrap() .len(), magic_wormhole::transit::Abilities::ALL_ABILITIES, + &transit::log_transit_connection, |_, _| {}, futures::future::pending(), ) @@ -236,6 +239,7 @@ pub async fn test_send_many() -> eyre::Result<()> { .unwrap() .len(), magic_wormhole::transit::Abilities::ALL_ABILITIES, + &transit::log_transit_connection, |_, _| {}, futures::future::pending(), ) @@ -263,8 +267,13 @@ pub async fn test_send_many() -> eyre::Result<()> { .unwrap(); let mut buffer = Vec::::new(); - req.accept(|_, _| {}, &mut buffer, futures::future::pending()) - .await?; + req.accept( + &transit::log_transit_connection, + |_, _| {}, + &mut buffer, + futures::future::pending(), + ) + .await?; assert_eq!(correct_data, buffer, "Files #{} differ", i); } diff --git a/src/forwarding.rs b/src/forwarding.rs index a643019d..55dc45cf 100644 --- a/src/forwarding.rs +++ b/src/forwarding.rs @@ -135,6 +135,7 @@ impl ForwardingError { /// as the value. pub async fn serve( mut wormhole: Wormhole, + transit_handler: impl FnOnce(transit::TransitInfo, std::net::SocketAddr), relay_hints: Vec, targets: Vec<(Option, u16)>, cancel: impl Future, @@ -189,7 +190,7 @@ pub async fn serve( }, }; - let mut transit = match connector + let (mut transit, info, addr) = match connector .leader_connect( wormhole.key().derive_transit_key(wormhole.appid()), peer_version.transit_abilities, @@ -206,6 +207,7 @@ pub async fn serve( return Err(error); }, }; + transit_handler(info, addr); /* We got a transit, now close the Wormhole */ wormhole.close().await?; @@ -516,6 +518,7 @@ impl ForwardingServe { /// no more than 1024 ports may be forwarded at once. pub async fn connect( mut wormhole: Wormhole, + transit_handler: impl FnOnce(transit::TransitInfo, std::net::SocketAddr), relay_hints: Vec, bind_address: Option, custom_ports: &[u16], @@ -558,7 +561,7 @@ pub async fn connect( }, }; - let mut transit = match connector + let (mut transit, info, addr) = match connector .follower_connect( wormhole.key().derive_transit_key(wormhole.appid()), peer_version.transit_abilities, @@ -575,6 +578,7 @@ pub async fn connect( return Err(error); }, }; + transit_handler(info, addr); /* We got a transit, now close the Wormhole */ wormhole.close().await?; diff --git a/src/lib.rs b/src/lib.rs index 7bd285bb..781dca67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ #![forbid(unsafe_code)] #![allow(clippy::upper_case_acronyms)] +#![allow(clippy::too_many_arguments)] #[macro_use] mod util; diff --git a/src/transfer.rs b/src/transfer.rs index d02c0515..3e0939c5 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -201,18 +201,20 @@ impl TransitAck { } } -pub async fn send_file_or_folder( +pub async fn send_file_or_folder( wormhole: Wormhole, relay_url: url::Url, file_path: N, file_name: M, transit_abilities: transit::Abilities, + transit_handler: G, progress_handler: H, cancel: impl Future, ) -> Result<(), TransferError> where N: AsRef, M: AsRef, + G: FnOnce(transit::TransitInfo, std::net::SocketAddr), H: FnMut(u64, u64) + 'static, { use async_std::fs::File; @@ -228,6 +230,7 @@ where file_path, file_name, transit_abilities, + transit_handler, progress_handler, cancel, ) @@ -241,6 +244,7 @@ where file_name, file_size, transit_abilities, + transit_handler, progress_handler, cancel, ) @@ -253,19 +257,21 @@ where /// /// You must ensure that the Reader contains exactly as many bytes /// as advertized in file_size. -pub async fn send_file( +pub async fn send_file( wormhole: Wormhole, relay_url: url::Url, file: &mut F, file_name: N, file_size: u64, transit_abilities: transit::Abilities, + transit_handler: G, progress_handler: H, cancel: impl Future, ) -> Result<(), TransferError> where F: AsyncRead + Unpin, N: Into, + G: FnOnce(transit::TransitInfo, std::net::SocketAddr), H: FnMut(u64, u64) + 'static, { let _peer_version: AppVersion = serde_json::from_value(wormhole.peer_version.clone())?; @@ -281,6 +287,7 @@ where file_name, file_size, transit_abilities, + transit_handler, progress_handler, cancel, ) @@ -293,18 +300,20 @@ where /// This isn't a proper folder transfer as per the Wormhole protocol /// because it sends it in a way so that the receiver still has to manually /// unpack it. But it's better than nothing -pub async fn send_folder( +pub async fn send_folder( wormhole: Wormhole, relay_url: url::Url, folder_path: N, folder_name: M, transit_abilities: transit::Abilities, + transit_handler: G, progress_handler: H, cancel: impl Future, ) -> Result<(), TransferError> where N: Into, M: Into, + G: FnOnce(transit::TransitInfo, std::net::SocketAddr), H: FnMut(u64, u64) + 'static, { let relay_hints = vec![transit::RelayHint::from_urls(None, [relay_url])]; @@ -314,6 +323,7 @@ where folder_path, folder_name, transit_abilities, + transit_handler, progress_handler, cancel, ) @@ -437,14 +447,16 @@ impl ReceiveRequest { * * This will transfer the file and save it on disk. */ - pub async fn accept( + pub async fn accept( mut self, + transit_handler: G, progress_handler: F, content_handler: &mut W, cancel: impl Future, ) -> Result<(), TransferError> where F: FnMut(u64, u64) + 'static, + G: FnOnce(transit::TransitInfo, std::net::SocketAddr), W: AsyncWrite + Unpin, { let run = async { @@ -454,7 +466,7 @@ impl ReceiveRequest { .send_json(&PeerMessage::file_ack("ok")) .await?; - let mut transit = self + let (mut transit, info, addr) = self .connector .follower_connect( self.wormhole @@ -464,6 +476,7 @@ impl ReceiveRequest { self.their_hints.clone(), ) .await?; + transit_handler(info, addr); debug!("Beginning file transfer"); v1::tcp_file_receive( diff --git a/src/transfer/v1.rs b/src/transfer/v1.rs index 7b136f27..0fe45039 100644 --- a/src/transfer/v1.rs +++ b/src/transfer/v1.rs @@ -5,19 +5,21 @@ use std::path::PathBuf; use super::*; -pub async fn send_file( +pub async fn send_file( mut wormhole: Wormhole, relay_hints: Vec, file: &mut F, file_name: N, file_size: u64, transit_abilities: transit::Abilities, + transit_handler: G, progress_handler: H, cancel: impl Future, ) -> Result<(), TransferError> where F: AsyncRead + Unpin, N: Into, + G: FnOnce(transit::TransitInfo, std::net::SocketAddr), H: FnMut(u64, u64) + 'static, { let run = async { @@ -74,13 +76,14 @@ where } } - let mut transit = connector + let (mut transit, info, addr) = connector .leader_connect( wormhole.key().derive_transit_key(wormhole.appid()), their_abilities, Arc::new(their_hints), ) .await?; + transit_handler(info, addr); debug!("Beginning file transfer"); @@ -130,18 +133,20 @@ where } } -pub async fn send_folder( +pub async fn send_folder( mut wormhole: Wormhole, relay_hints: Vec, folder_path: N, folder_name: M, transit_abilities: transit::Abilities, + transit_handler: G, progress_handler: H, cancel: impl Future, ) -> Result<(), TransferError> where N: Into, M: Into, + G: FnOnce(transit::TransitInfo, std::net::SocketAddr), H: FnMut(u64, u64) + 'static, { let run = async { @@ -249,13 +254,14 @@ where }, } - let mut transit = connector + let (mut transit, info, addr) = connector .leader_connect( wormhole.key().derive_transit_key(wormhole.appid()), their_abilities, Arc::new(their_hints), ) .await?; + transit_handler(info, addr); debug!("Beginning file transfer"); diff --git a/src/transit.rs b/src/transit.rs index 7636d883..7d19cf88 100644 --- a/src/transit.rs +++ b/src/transit.rs @@ -23,7 +23,11 @@ use async_std::{ #[allow(unused_imports)] /* We need them for the docs */ use futures::{future::TryFutureExt, Sink, SinkExt, Stream, StreamExt, TryStreamExt}; use log::*; -use std::{collections::HashSet, sync::Arc}; +use std::{ + collections::HashSet, + net::{IpAddr, SocketAddr, ToSocketAddrs}, + sync::Arc, +}; use xsalsa20poly1305 as secretbox; use xsalsa20poly1305::aead::{Aead, NewAead}; @@ -470,28 +474,29 @@ impl<'de> serde::Deserialize<'de> for RelayHint { use std::convert::{TryFrom, TryInto}; -impl TryFrom<&DirectHint> for std::net::IpAddr { +impl TryFrom<&DirectHint> for IpAddr { type Error = std::net::AddrParseError; - fn try_from(hint: &DirectHint) -> Result { + fn try_from(hint: &DirectHint) -> Result { hint.hostname.parse() } } -impl TryFrom<&DirectHint> for std::net::SocketAddr { +impl TryFrom<&DirectHint> for SocketAddr { type Error = std::net::AddrParseError; /** This does not do the obvious thing and also implicitly maps all V4 addresses into V6 */ - fn try_from(hint: &DirectHint) -> Result { + fn try_from(hint: &DirectHint) -> Result { let addr = hint.try_into()?; let addr = match addr { - std::net::IpAddr::V4(v4) => std::net::IpAddr::V6(v4.to_ipv6_mapped()), - std::net::IpAddr::V6(_) => addr, + IpAddr::V4(v4) => IpAddr::V6(v4.to_ipv6_mapped()), + IpAddr::V6(_) => addr, }; - Ok(std::net::SocketAddr::new(addr, hint.port)) + Ok(SocketAddr::new(addr, hint.port)) } } #[derive(Clone, Debug, Eq, PartialEq)] -enum TransitInfo { +#[non_exhaustive] +pub enum TransitInfo { Direct, Relay { name: Option }, } @@ -583,20 +588,19 @@ enum StunError { } /** Perform a STUN query to get the external IP address */ -async fn get_external_ip() -> Result<(std::net::SocketAddr, TcpStream), StunError> { +async fn get_external_ip() -> Result<(SocketAddr, TcpStream), StunError> { let mut socket = connect_custom( - &"[::]:0".parse::().unwrap().into(), + &"[::]:0".parse::().unwrap().into(), &PUBLIC_STUN_SERVER .to_socket_addrs()? /* If you find yourself behind a NAT66, open an issue */ .find(|x| x.is_ipv4()) /* TODO add a helper method to stdlib for this */ .map(|addr| match addr { - std::net::SocketAddr::V4(v4) => std::net::SocketAddr::new( - std::net::IpAddr::V6(v4.ip().to_ipv6_mapped()), - v4.port(), - ), - std::net::SocketAddr::V6(_) => unreachable!(), + SocketAddr::V4(v4) => { + SocketAddr::new(IpAddr::V6(v4.ip().to_ipv6_mapped()), v4.port()) + }, + SocketAddr::V6(_) => unreachable!(), }) .ok_or(StunError::ServerIsV4Only)? .into(), @@ -604,7 +608,6 @@ async fn get_external_ip() -> Result<(std::net::SocketAddr, TcpStream), StunErro .await?; use bytecodec::{DecodeExt, EncodeExt}; - use std::net::{SocketAddr, ToSocketAddrs}; use stun_codec::{ rfc5389::{ self, @@ -668,6 +671,38 @@ async fn get_external_ip() -> Result<(std::net::SocketAddr, TcpStream), StunErro Ok((external_addr, socket)) } +/// Utility method that logs information of the transit result +/// +/// Example usage: +/// +/// ```no_run +/// # let derived_key = todo!(); +/// # let their_abilities = todo!(); +/// # let their_hints = todo!(); +/// let connector: transit::TransitConnector = todo!("transit::init(…).await?"); +/// let (mut transit, info, addr) = connector +/// .leader_connect(derived_key, their_abilities, their_hints) +/// .await?; +/// transit::log_transit_connection(info, addr); +/// ``` +pub fn log_transit_connection(info: TransitInfo, peer_addr: SocketAddr) { + match info { + TransitInfo::Direct => { + log::info!("Established direct transit connection to '{}'", peer_addr,); + }, + TransitInfo::Relay { name: Some(name) } => { + log::info!( + "Established transit connection via relay '{}' ({})", + name, + peer_addr, + ); + }, + TransitInfo::Relay { name: None } => { + log::info!("Established transit connection via relay ({})", peer_addr,); + }, + } +} + /** * Initialize a relay handshake * @@ -714,7 +749,7 @@ pub async fn init( set_socket_opts(&socket)?; socket - .bind(&"[::]:0".parse::().unwrap().into()) + .bind(&"[::]:0".parse::().unwrap().into()) .unwrap(); socket.into() @@ -817,7 +852,7 @@ impl TransitConnector { transit_key: Key, their_abilities: Abilities, their_hints: Arc, - ) -> Result { + ) -> Result<(Transit, TransitInfo, SocketAddr), TransitConnectError> { let Self { sockets, our_abilities, @@ -895,29 +930,9 @@ impl TransitConnector { std::mem::drop(connection_stream); transit.socket.write_all(b"go\n").await?; - match host_type { - TransitInfo::Direct => { - log::info!( - "Established direct transit connection to '{}'", - transit.socket.peer_addr().unwrap() - ); - }, - TransitInfo::Relay { name: Some(name) } => { - log::info!( - "Established transit connection via relay '{}' ({})", - name, - transit.socket.peer_addr().unwrap() - ); - }, - TransitInfo::Relay { name: None } => { - log::info!( - "Established transit connection via relay ({})", - transit.socket.peer_addr().unwrap() - ); - }, - } - Ok(transit) + let addr = transit.socket.peer_addr().unwrap(); + Ok((transit, host_type, addr)) } /** @@ -928,7 +943,7 @@ impl TransitConnector { transit_key: Key, their_abilities: Abilities, their_hints: Arc, - ) -> Result { + ) -> Result<(Transit, TransitInfo, SocketAddr), TransitConnectError> { let Self { sockets, our_abilities, @@ -964,28 +979,8 @@ impl TransitConnector { .await { Ok(Some((transit, host_type))) => { - match host_type { - TransitInfo::Direct => { - log::info!( - "Established direct transit connection to '{}'", - transit.socket.peer_addr().unwrap() - ); - }, - TransitInfo::Relay { name: Some(name) } => { - log::info!( - "Established transit connection via relay '{}' ({})", - name, - transit.socket.peer_addr().unwrap() - ); - }, - TransitInfo::Relay { name: None } => { - log::info!( - "Established transit connection via relay ({})", - transit.socket.peer_addr().unwrap() - ); - }, - } - Ok(transit) + let addr = transit.socket.peer_addr().unwrap(); + Ok((transit, host_type, addr)) }, Ok(None) | Err(_) => { log::debug!("`follower_connect` timed out"); @@ -1049,7 +1044,7 @@ impl TransitConnector { .map(move |hint| { let local_addr = local_addr.clone(); async move { - let dest_addr = std::net::SocketAddr::try_from(&hint)?; + let dest_addr = SocketAddr::try_from(&hint)?; log::debug!("Connecting directly to {}", dest_addr); let socket = connect_custom(&local_addr, &dest_addr.into()).await?; log::debug!("Connected to {}!", dest_addr); From 0a971831c61b6559c773702d169479b20a5638d1 Mon Sep 17 00:00:00 2001 From: piegames Date: Mon, 2 May 2022 16:29:25 +0200 Subject: [PATCH 08/14] fixup! Bump dependencies --- Cargo.lock | 34 +++------------------------------- cli/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa5cb0ab..daffe7b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,34 +170,6 @@ dependencies = [ "wasm-bindgen-futures", ] -[[package]] -name = "async-std" -version = "1.11.0" -source = "git+https://github.com/async-rs/async-std#2c6304662b96aab64feb20f7d727bfc8e070453e" -dependencies = [ - "async-attributes", - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "num_cpus", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-task" version = "4.2.0" @@ -223,7 +195,7 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7922abeade7dd8948c20dfa1f85dc48cc952d2e0791f7c42b8b1cbb07a57129d" dependencies = [ - "async-std 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std", "async-tls", "futures-io", "futures-util", @@ -1143,7 +1115,7 @@ name = "magic-wormhole" version = "0.4.0" dependencies = [ "async-io", - "async-std 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std", "async-tungstenite", "base64", "bytecodec", @@ -2416,7 +2388,7 @@ dependencies = [ name = "wormhole-rs" version = "0.4.0" dependencies = [ - "async-std 1.11.0 (git+https://github.com/async-rs/async-std)", + "async-std", "clap", "cli-clipboard", "color-eyre", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 7be879a4..06349f24 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -10,7 +10,7 @@ serde_derive = "1.0.120" log = "0.4.13" url = { version = "2.2.2", features = ["serde"] } futures = "0.3.12" -async-std = { version = "1.9.0", features = ["attributes", "unstable"], git = "https://github.com/async-rs/async-std" } +async-std = { version = "1.11.0", features = ["attributes", "unstable"] } # CLI specific dependencies magic-wormhole = { path = "..", features = ["all"] } From e1ec0d38624d66953da61e2539ed1b05a9119976 Mon Sep 17 00:00:00 2001 From: piegames Date: Fri, 6 May 2022 10:15:32 +0200 Subject: [PATCH 09/14] fixup! Transit: expose information about the established connection --- src/core/test.rs | 2 ++ src/transfer.rs | 6 +++--- src/transit.rs | 8 ++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/core/test.rs b/src/core/test.rs index cd4be71e..6c69dec3 100644 --- a/src/core/test.rs +++ b/src/core/test.rs @@ -132,6 +132,7 @@ pub async fn test_4096_file_rust2rust() -> eyre::Result<()> { "example-file.bin", std::fs::metadata(FILENAME).unwrap().len(), magic_wormhole::transit::Abilities::ALL_ABILITIES, + &transit::log_transit_connection, |_sent, _total| {}, futures::future::pending(), ) @@ -160,6 +161,7 @@ pub async fn test_4096_file_rust2rust() -> eyre::Result<()> { let mut buffer = Vec::::new(); req.accept( + &transit::log_transit_connection, |_received, _total| {}, &mut buffer, futures::future::pending(), diff --git a/src/transfer.rs b/src/transfer.rs index 3e0939c5..228f6425 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -126,9 +126,9 @@ impl TransferError { #[serde(rename_all = "kebab-case")] pub struct AppVersion { // #[serde(default)] -// abilities: Cow<'static, [Cow<'static, str>]>, -// #[serde(default)] -// transfer_v2: Option, + // abilities: Cow<'static, [Cow<'static, str>]>, + // #[serde(default)] + // transfer_v2: Option, } // TODO check invariants during deserialization diff --git a/src/transit.rs b/src/transit.rs index 7d19cf88..d18f512a 100644 --- a/src/transit.rs +++ b/src/transit.rs @@ -676,14 +676,18 @@ async fn get_external_ip() -> Result<(SocketAddr, TcpStream), StunError> { /// Example usage: /// /// ```no_run +/// use magic_wormhole as mw; +/// # #[async_std::main] async fn main() -> Result<(), mw::transit::TransitConnectError> { /// # let derived_key = todo!(); /// # let their_abilities = todo!(); /// # let their_hints = todo!(); -/// let connector: transit::TransitConnector = todo!("transit::init(…).await?"); +/// let connector: mw::transit::TransitConnector = todo!("transit::init(…).await?"); /// let (mut transit, info, addr) = connector /// .leader_connect(derived_key, their_abilities, their_hints) /// .await?; -/// transit::log_transit_connection(info, addr); +/// mw::transit::log_transit_connection(info, addr); +/// # Ok(()) +/// # } /// ``` pub fn log_transit_connection(info: TransitInfo, peer_addr: SocketAddr) { match info { From 3d1166f39514e51bc472ea2172aa810a49c2a771 Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Thu, 28 Apr 2022 17:34:22 +0800 Subject: [PATCH 10/14] added send_file example --- src/transfer.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/transfer.rs b/src/transfer.rs index 228f6425..5979d112 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -257,6 +257,45 @@ where /// /// You must ensure that the Reader contains exactly as many bytes /// as advertized in file_size. +/// +/// # Example +/// +/// ```no_run +/// # use magic_wormhole::{Wormhole, WormholeError}; +/// use magic_wormhole::transfer::{APP_CONFIG, send_file}; +/// use magic_wormhole::transit; +/// use async_std::fs::File; +/// +/// # #[async_std::main] +/// # async fn main() -> Result<(), Box> { +/// let filepath = "foobar.baz"; +/// let mut file = File::create(filepath).await.unwrap(); +/// let file_length = file.metadata().await.unwrap().len(); +/// +/// // Wormhole connection +/// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; +/// let connector = wormhole.await.unwrap(); +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse().unwrap(); +/// +/// // Transfer status +/// let mut bytes_sent = 0; +/// let mut bytes_total = 0; +/// let progress = move |sent, total| { +/// bytes_sent = sent; +/// bytes_total = total; +/// }; +/// +/// let cancel = futures::future::pending(); +/// +/// send_file( +/// connector, relay_url, +/// &mut file, filepath, file_length, +/// transit::Abilities::ALL_ABILITIES, progress, cancel, +/// ).await; +/// +/// # std::fs::remove_file(filepath).unwrap(); //cleanup +/// # Ok(()) +/// # } pub async fn send_file( wormhole: Wormhole, relay_url: url::Url, From 72f259fee5af5e289665b71dfcf6e0d41686f413 Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Thu, 28 Apr 2022 17:35:09 +0800 Subject: [PATCH 11/14] added wormhole connection examples --- src/core.rs | 53 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/core.rs b/src/core.rs index 8a2c695b..d2069641 100644 --- a/src/core.rs +++ b/src/core.rs @@ -117,15 +117,28 @@ pub struct Wormhole { } impl Wormhole { - /** - * Generate a code and connect to the rendezvous server. - * - * # Returns - * - * A tuple with a [`WormholeWelcome`] and a [`std::future::Future`] that will - * do the rest of the client-client handshake and yield the [`Wormhole`] object - * on success. - */ + /// Generate a code and connect to the rendezvous server, `code_length` is the + /// number of words after the nameplate + /// + /// # Returns + /// + /// A tuple with a [`WormholeWelcome`] and a [`std::future::Future`] that will + /// do the rest of the client-client handshake and yield the [`Wormhole`] object + /// on success. + /// + /// # Example + /// ``` + /// # #[async_std::main] + /// # async fn main() -> Result<(), magic_wormhole::WormholeError> { + /// # use magic_wormhole::Wormhole; + /// use magic_wormhole::transfer::APP_CONFIG; + /// + /// let num_words = 2; + /// let (welcome, wormhole) = Wormhole::connect_without_code(APP_CONFIG, num_words).await?; + /// let wormhole_code = welcome.code.0; + /// # Ok(()) + /// # } + /// ``` pub async fn connect_without_code( config: AppConfig, code_length: usize, @@ -160,9 +173,25 @@ impl Wormhole { )) } - /** - * Connect to a peer with a code. - */ + /// Connect to a peer with a code. + /// + /// # Returns + /// + /// A [`WormholeWelcome`] and a [`std::future::Future`] which yields + /// a Wormhole object + /// + /// # Example + /// ```no_run + /// # #[async_std::main] + /// # async fn main() -> Result<(), magic_wormhole::WormholeError> { + /// # use magic_wormhole::Wormhole; + /// use magic_wormhole::{Code, transfer::APP_CONFIG}; + /// + /// let code = Code("1-aardvark-Zulu".to_string()); + /// let (welcome, wormhole) = Wormhole::connect_with_code(APP_CONFIG, code).await?; + /// # Ok(()) + /// # } + /// ``` pub async fn connect_with_code( config: AppConfig, code: Code, From 84745fe48a8979e8d045ed961e8d4faf9437d184 Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Tue, 3 May 2022 15:06:21 +0800 Subject: [PATCH 12/14] added doc examples --- src/transfer.rs | 166 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 144 insertions(+), 22 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index 5979d112..e69946f5 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -200,8 +200,44 @@ impl TransitAck { serde_json::to_vec(self).unwrap() } } - -pub async fn send_file_or_folder( +/// Sends either a file or folder to the other side +/// +/// # Example +/// +/// ```no_run +/// # use magic_wormhole::{Wormhole, WormholeError}; +/// use magic_wormhole::transfer::{send_file_or_folder, APP_CONFIG}; +/// use magic_wormhole::transit; +/// +/// # #[async_std::main] +/// # async fn main() -> Result<(), Box> { +/// let folder_path = "./foo/bar"; +/// let folder_name = "bar"; +/// +/// // Wormhole connection +/// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; +/// let connector = wormhole.await?; +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; +/// +/// // Transfer status +/// let mut bytes_sent = 0; +/// let mut bytes_total = 0; +/// let progress = move |sent, total| { +/// bytes_sent = sent; +/// bytes_total = total; +/// }; +/// +/// let cancel = futures::future::pending(); +/// let abilities = transit::Abilities::ALL_ABILITIES; +/// +/// send_file_or_folder( +/// connector, relay_url, +/// folder_path, folder_name, +/// abilities, progress, cancel +/// ).await?; +/// # Ok(()) +/// # } +pub async fn send_file_or_folder( wormhole: Wormhole, relay_url: url::Url, file_path: N, @@ -262,20 +298,20 @@ where /// /// ```no_run /// # use magic_wormhole::{Wormhole, WormholeError}; -/// use magic_wormhole::transfer::{APP_CONFIG, send_file}; +/// use magic_wormhole::transfer::{send_file, APP_CONFIG}; /// use magic_wormhole::transit; /// use async_std::fs::File; /// /// # #[async_std::main] /// # async fn main() -> Result<(), Box> { -/// let filepath = "foobar.baz"; -/// let mut file = File::create(filepath).await.unwrap(); -/// let file_length = file.metadata().await.unwrap().len(); +/// let filepath = "foo.txt"; +/// let mut file = File::create(filepath).await?; +/// let file_length = file.metadata().await?.len(); /// /// // Wormhole connection /// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; -/// let connector = wormhole.await.unwrap(); -/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse().unwrap(); +/// let connector = wormhole.await?; +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; /// /// // Transfer status /// let mut bytes_sent = 0; @@ -286,14 +322,13 @@ where /// }; /// /// let cancel = futures::future::pending(); +/// let abilities = transit::Abilities::ALL_ABILITIES; /// /// send_file( /// connector, relay_url, /// &mut file, filepath, file_length, -/// transit::Abilities::ALL_ABILITIES, progress, cancel, -/// ).await; -/// -/// # std::fs::remove_file(filepath).unwrap(); //cleanup +/// abilities, progress, cancel +/// ).await?; /// # Ok(()) /// # } pub async fn send_file( @@ -339,7 +374,44 @@ where /// This isn't a proper folder transfer as per the Wormhole protocol /// because it sends it in a way so that the receiver still has to manually /// unpack it. But it's better than nothing -pub async fn send_folder( +/// +/// # Example +/// +/// ```no_run +/// # use magic_wormhole::{Wormhole, WormholeError}; +/// use magic_wormhole::transfer::{send_folder, APP_CONFIG}; +/// use magic_wormhole::transit; +/// +/// # #[async_std::main] +/// # async fn main() -> Result<(), Box> { +/// let folder_path = "./foo/bar"; +/// let folder_name = "bar"; +/// +/// // Wormhole connection +/// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; +/// let connector = wormhole.await?; +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; +/// +/// // Transfer status +/// let mut bytes_sent = 0; +/// let mut bytes_total = 0; +/// let progress = move |sent, total| { +/// bytes_sent = sent; +/// bytes_total = total; +/// }; +/// +/// let cancel = futures::future::pending(); +/// let abilities = transit::Abilities::ALL_ABILITIES; +/// +/// send_folder( +/// connector, relay_url, +/// folder_path, folder_name, +/// abilities, progress, cancel +/// ).await?; +/// +/// # Ok(()) +/// # } +pub async fn send_folder( wormhole: Wormhole, relay_url: url::Url, folder_path: N, @@ -369,14 +441,32 @@ where .await } -/** - * Wait for a file offer from the other side - * - * This method waits for an offer message and builds up a [`ReceiveRequest`](ReceiveRequest). - * It will also start building a TCP connection to the other side using the transit protocol. - * - * Returns `None` if the task got cancelled. - */ +/// Wait for a file offer from the other side +/// +/// This method waits for an offer message and builds up a [`ReceiveRequest`](ReceiveRequest). +/// It will also start building a TCP connection to the other side using the transit protocol. +/// +/// Returns `None` if the task got cancelled. +/// +/// # Example +/// +/// ```no_run +/// # use magic_wormhole::{Wormhole, WormholeError}; +/// use magic_wormhole::transfer::{request_file, APP_CONFIG}; +/// use magic_wormhole::transit; +/// +/// # #[async_std::main] +/// # async fn main() -> Result<(), Box> { +/// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; +/// let mut connector = wormhole.await?; +/// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; +/// +/// let abilities = transit::Abilities::ALL_ABILITIES; +/// let cancel = futures::future::pending(); +/// +/// let mut receive_request = request_file(connector, relay_url, abilities, cancel).await?; +/// # Ok(()) +/// # } pub async fn request_file( mut wormhole: Wormhole, relay_url: url::Url, @@ -468,6 +558,8 @@ pub async fn request_file( * A pending files send offer from the other side * * You *should* consume this object, either by calling [`accept`](ReceiveRequest::accept) or [`reject`](ReceiveRequest::reject). + * + * Constructed from the [`request_file`](self::request_file) function. */ #[must_use] pub struct ReceiveRequest { @@ -486,7 +578,37 @@ impl ReceiveRequest { * * This will transfer the file and save it on disk. */ - pub async fn accept( + /// # Example + /// + /// ```no_run + /// # use magic_wormhole::{Wormhole, WormholeError}; + /// # use magic_wormhole::transfer::{request_file, APP_CONFIG}; + /// # use magic_wormhole::transit; + /// use async_std::fs::File; + /// + /// # #[async_std::main] + /// # async fn main() -> Result<(), Box> { + /// # let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; + /// # let mut wormhole = wormhole.await?; + /// # let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; + /// # let abilities = transit::Abilities::ALL_ABILITIES; + /// # let cancel = futures::future::pending(); + /// let mut receive_request = request_file(wormhole, relay_url, abilities, cancel).await?; + /// let mut file = File::create("foo.txt").await?; + /// + /// let mut bytes_sent = 0; + /// let mut bytes_total = 0; + /// let progress = move |sent, total| { + /// bytes_sent = sent; + /// bytes_total = total; + /// }; + /// let cancel = futures::future::pending(); + /// + /// receive_request.unwrap().accept(progress, &mut file, cancel); + /// + /// # Ok(()) + /// # } + pub async fn accept( mut self, transit_handler: G, progress_handler: F, From 4472c5b2e8e0e3e3f9b1093be90934b941ac069c Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Tue, 17 May 2022 10:50:52 +0800 Subject: [PATCH 13/14] updated doc examples --- src/transfer.rs | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/transfer.rs b/src/transfer.rs index e69946f5..8ef87a7c 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -231,13 +231,18 @@ impl TransitAck { /// let abilities = transit::Abilities::ALL_ABILITIES; /// /// send_file_or_folder( -/// connector, relay_url, -/// folder_path, folder_name, -/// abilities, progress, cancel +/// connector, +/// relay_url, +/// folder_path, +/// folder_name, +/// abilities, +/// transit::log_transit_connection, +/// progress, +/// cancel /// ).await?; /// # Ok(()) /// # } -pub async fn send_file_or_folder( +pub async fn send_file_or_folder( wormhole: Wormhole, relay_url: url::Url, file_path: N, @@ -325,9 +330,15 @@ where /// let abilities = transit::Abilities::ALL_ABILITIES; /// /// send_file( -/// connector, relay_url, -/// &mut file, filepath, file_length, -/// abilities, progress, cancel +/// connector, +/// relay_url, +/// &mut file, +/// filepath, +/// file_length, +/// abilities, +/// transit::log_transit_connection, +/// progress, +/// cancel /// ).await?; /// # Ok(()) /// # } @@ -404,14 +415,19 @@ where /// let abilities = transit::Abilities::ALL_ABILITIES; /// /// send_folder( -/// connector, relay_url, -/// folder_path, folder_name, -/// abilities, progress, cancel +/// connector, +/// relay_url, +/// folder_path, +/// folder_name, +/// abilities, +/// transit::log_transit_connection, +/// progress, +/// cancel /// ).await?; /// /// # Ok(()) /// # } -pub async fn send_folder( +pub async fn send_folder( wormhole: Wormhole, relay_url: url::Url, folder_path: N, @@ -604,11 +620,16 @@ impl ReceiveRequest { /// }; /// let cancel = futures::future::pending(); /// - /// receive_request.unwrap().accept(progress, &mut file, cancel); + /// receive_request.unwrap().accept( + /// transit::log_transit_connection, + /// progress, + /// &mut file, + /// cancel + /// ); /// /// # Ok(()) /// # } - pub async fn accept( + pub async fn accept( mut self, transit_handler: G, progress_handler: F, From f50c56f209b0e5823516af39d678542d6a5cc449 Mon Sep 17 00:00:00 2001 From: Mitch Berry Date: Tue, 17 May 2022 11:10:41 +0800 Subject: [PATCH 14/14] cargo fmt cleanup --- src/core.rs | 60 ++++++++++++++++++------------------- src/transfer.rs | 78 ++++++++++++++++++++++++------------------------- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/core.rs b/src/core.rs index d2069641..a85e077a 100644 --- a/src/core.rs +++ b/src/core.rs @@ -117,28 +117,28 @@ pub struct Wormhole { } impl Wormhole { - /// Generate a code and connect to the rendezvous server, `code_length` is the - /// number of words after the nameplate - /// - /// # Returns - /// - /// A tuple with a [`WormholeWelcome`] and a [`std::future::Future`] that will - /// do the rest of the client-client handshake and yield the [`Wormhole`] object - /// on success. - /// - /// # Example - /// ``` - /// # #[async_std::main] - /// # async fn main() -> Result<(), magic_wormhole::WormholeError> { - /// # use magic_wormhole::Wormhole; - /// use magic_wormhole::transfer::APP_CONFIG; - /// - /// let num_words = 2; - /// let (welcome, wormhole) = Wormhole::connect_without_code(APP_CONFIG, num_words).await?; - /// let wormhole_code = welcome.code.0; - /// # Ok(()) - /// # } - /// ``` + /// Generate a code and connect to the rendezvous server, `code_length` is the + /// number of words after the nameplate + /// + /// # Returns + /// + /// A tuple with a [`WormholeWelcome`] and a [`std::future::Future`] that will + /// do the rest of the client-client handshake and yield the [`Wormhole`] object + /// on success. + /// + /// # Example + /// ``` + /// # #[async_std::main] + /// # async fn main() -> Result<(), magic_wormhole::WormholeError> { + /// # use magic_wormhole::Wormhole; + /// use magic_wormhole::transfer::APP_CONFIG; + /// + /// let num_words = 2; + /// let (welcome, wormhole) = Wormhole::connect_without_code(APP_CONFIG, num_words).await?; + /// let wormhole_code = welcome.code.0; + /// # Ok(()) + /// # } + /// ``` pub async fn connect_without_code( config: AppConfig, code_length: usize, @@ -174,22 +174,22 @@ impl Wormhole { } /// Connect to a peer with a code. - /// + /// /// # Returns /// - /// A [`WormholeWelcome`] and a [`std::future::Future`] which yields + /// A [`WormholeWelcome`] and a [`std::future::Future`] which yields /// a Wormhole object - /// + /// /// # Example /// ```no_run /// # #[async_std::main] /// # async fn main() -> Result<(), magic_wormhole::WormholeError> { /// # use magic_wormhole::Wormhole; - /// use magic_wormhole::{Code, transfer::APP_CONFIG}; - /// - /// let code = Code("1-aardvark-Zulu".to_string()); - /// let (welcome, wormhole) = Wormhole::connect_with_code(APP_CONFIG, code).await?; - /// # Ok(()) + /// use magic_wormhole::{transfer::APP_CONFIG, Code}; + /// + /// let code = Code("1-aardvark-Zulu".to_string()); + /// let (welcome, wormhole) = Wormhole::connect_with_code(APP_CONFIG, code).await?; + /// # Ok(()) /// # } /// ``` pub async fn connect_with_code( diff --git a/src/transfer.rs b/src/transfer.rs index 8ef87a7c..566b63b6 100644 --- a/src/transfer.rs +++ b/src/transfer.rs @@ -201,24 +201,24 @@ impl TransitAck { } } /// Sends either a file or folder to the other side -/// +/// /// # Example -/// +/// /// ```no_run /// # use magic_wormhole::{Wormhole, WormholeError}; /// use magic_wormhole::transfer::{send_file_or_folder, APP_CONFIG}; /// use magic_wormhole::transit; -/// +/// /// # #[async_std::main] /// # async fn main() -> Result<(), Box> { /// let folder_path = "./foo/bar"; /// let folder_name = "bar"; -/// +/// /// // Wormhole connection /// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; /// let connector = wormhole.await?; /// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; -/// +/// /// // Transfer status /// let mut bytes_sent = 0; /// let mut bytes_total = 0; @@ -231,13 +231,13 @@ impl TransitAck { /// let abilities = transit::Abilities::ALL_ABILITIES; /// /// send_file_or_folder( -/// connector, +/// connector, /// relay_url, -/// folder_path, +/// folder_path, /// folder_name, -/// abilities, +/// abilities, /// transit::log_transit_connection, -/// progress, +/// progress, /// cancel /// ).await?; /// # Ok(()) @@ -298,26 +298,26 @@ where /// /// You must ensure that the Reader contains exactly as many bytes /// as advertized in file_size. -/// +/// /// # Example -/// +/// /// ```no_run /// # use magic_wormhole::{Wormhole, WormholeError}; /// use magic_wormhole::transfer::{send_file, APP_CONFIG}; /// use magic_wormhole::transit; /// use async_std::fs::File; -/// +/// /// # #[async_std::main] /// # async fn main() -> Result<(), Box> { /// let filepath = "foo.txt"; /// let mut file = File::create(filepath).await?; /// let file_length = file.metadata().await?.len(); -/// +/// /// // Wormhole connection /// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; /// let connector = wormhole.await?; /// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; -/// +/// /// // Transfer status /// let mut bytes_sent = 0; /// let mut bytes_total = 0; @@ -330,14 +330,14 @@ where /// let abilities = transit::Abilities::ALL_ABILITIES; /// /// send_file( -/// connector, +/// connector, /// relay_url, -/// &mut file, -/// filepath, +/// &mut file, +/// filepath, /// file_length, /// abilities, -/// transit::log_transit_connection, -/// progress, +/// transit::log_transit_connection, +/// progress, /// cancel /// ).await?; /// # Ok(()) @@ -385,24 +385,24 @@ where /// This isn't a proper folder transfer as per the Wormhole protocol /// because it sends it in a way so that the receiver still has to manually /// unpack it. But it's better than nothing -/// +/// /// # Example -/// +/// /// ```no_run /// # use magic_wormhole::{Wormhole, WormholeError}; /// use magic_wormhole::transfer::{send_folder, APP_CONFIG}; /// use magic_wormhole::transit; -/// +/// /// # #[async_std::main] /// # async fn main() -> Result<(), Box> { /// let folder_path = "./foo/bar"; /// let folder_name = "bar"; -/// +/// /// // Wormhole connection /// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; /// let connector = wormhole.await?; /// let relay_url = transit::DEFAULT_RELAY_SERVER.parse()?; -/// +/// /// // Transfer status /// let mut bytes_sent = 0; /// let mut bytes_total = 0; @@ -415,16 +415,16 @@ where /// let abilities = transit::Abilities::ALL_ABILITIES; /// /// send_folder( -/// connector, +/// connector, /// relay_url, -/// folder_path, +/// folder_path, /// folder_name, -/// abilities, -/// transit::log_transit_connection, -/// progress, +/// abilities, +/// transit::log_transit_connection, +/// progress, /// cancel /// ).await?; -/// +/// /// # Ok(()) /// # } pub async fn send_folder( @@ -465,12 +465,12 @@ where /// Returns `None` if the task got cancelled. /// /// # Example -/// +/// /// ```no_run /// # use magic_wormhole::{Wormhole, WormholeError}; /// use magic_wormhole::transfer::{request_file, APP_CONFIG}; /// use magic_wormhole::transit; -/// +/// /// # #[async_std::main] /// # async fn main() -> Result<(), Box> { /// let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; @@ -574,7 +574,7 @@ pub async fn request_file( * A pending files send offer from the other side * * You *should* consume this object, either by calling [`accept`](ReceiveRequest::accept) or [`reject`](ReceiveRequest::reject). - * + * * Constructed from the [`request_file`](self::request_file) function. */ #[must_use] @@ -593,15 +593,15 @@ impl ReceiveRequest { * Accept the file offer * * This will transfer the file and save it on disk. - */ + * */ /// # Example - /// + /// /// ```no_run /// # use magic_wormhole::{Wormhole, WormholeError}; /// # use magic_wormhole::transfer::{request_file, APP_CONFIG}; /// # use magic_wormhole::transit; /// use async_std::fs::File; - /// + /// /// # #[async_std::main] /// # async fn main() -> Result<(), Box> { /// # let (_, wormhole) = Wormhole::connect_without_code(APP_CONFIG, 2).await?; @@ -610,7 +610,7 @@ impl ReceiveRequest { /// # let abilities = transit::Abilities::ALL_ABILITIES; /// # let cancel = futures::future::pending(); /// let mut receive_request = request_file(wormhole, relay_url, abilities, cancel).await?; - /// let mut file = File::create("foo.txt").await?; + /// let mut file = File::create("foo.txt").await?; /// /// let mut bytes_sent = 0; /// let mut bytes_total = 0; @@ -622,8 +622,8 @@ impl ReceiveRequest { /// /// receive_request.unwrap().accept( /// transit::log_transit_connection, - /// progress, - /// &mut file, + /// progress, + /// &mut file, /// cancel /// ); ///