diff --git a/soulseek-rs-lib/src/client.rs b/soulseek-rs-lib/src/client.rs index 270d864..875f7ce 100644 --- a/soulseek-rs-lib/src/client.rs +++ b/soulseek-rs-lib/src/client.rs @@ -1174,9 +1174,9 @@ impl Client { if failed { let reason = if peer_registered { - "Peer rejected the download request" + "The user declined the download" } else { - "Could not connect to the peer" + "Could not connect to the user" }; let _ = download .sender @@ -1213,7 +1213,7 @@ impl Client { .map(|d| (d.token, d.sender.clone())) .collect(); for (token, sender) in doomed { - let reason = Some("Peer disconnected".to_string()); + let reason = Some("The user went offline".to_string()); let _ = sender.send(DownloadStatus::Failed(reason.clone())); context.update_download_with_status( token, @@ -1274,7 +1274,7 @@ impl Client { context.downloads.update_status( token, DownloadStatus::Failed(Some( - "Peer reported the upload failed".to_string(), + "The upload failed on the other side".to_string(), )), ); context.downloads.remove(token); diff --git a/soulseek-rs-lib/src/download_store.rs b/soulseek-rs-lib/src/download_store.rs index f02a70e..e5fcf60 100644 --- a/soulseek-rs-lib/src/download_store.rs +++ b/soulseek-rs-lib/src/download_store.rs @@ -150,7 +150,7 @@ pub fn collect_failed_tokens( }) .map(|d| { let _ = d.sender.send(DownloadStatus::Failed(Some( - "Peer reported the upload failed".to_string(), + "The upload failed on the other side".to_string(), ))); d.token }) diff --git a/soulseek-rs-lib/tests/e2e.rs b/soulseek-rs-lib/tests/e2e.rs index 6760530..912ebf4 100644 --- a/soulseek-rs-lib/tests/e2e.rs +++ b/soulseek-rs-lib/tests/e2e.rs @@ -1274,3 +1274,64 @@ fn browse_a_peers_shared_files() { let _ = std::fs::remove_dir_all(share_dir); } + +// Browsing a peer that is NOT listening exercises the server-brokered +// (firewalled) path: the direct dial fails, the client asks the server to +// broker, and the peer connects back to our listener. This is the path that +// matters on the real network, where most peers are firewalled. The browser +// MUST be listening so the peer can connect back. +#[test] +fn browse_a_firewalled_peer_via_broker() { + let server = server_or_skip!(); + + let share_dir = unique_download_dir(); + std::fs::create_dir_all(share_dir.join("album")).unwrap(); + std::fs::write(share_dir.join("album").join("hidden.flac"), b"xxxx") + .unwrap(); + + // Sharer does NOT listen (firewalled): the browser's direct dial will fail, + // forcing the server-brokered connect-back. + let mut sharer = Client::with_settings(ClientSettings { + shared_directory: Some(share_dir.display().to_string()), + ..server.settings("e2e_fw_sharer", "pw") + }); + sharer.connect().expect("sharer connect"); + assert!(sharer.login().expect("sharer login")); + + // Browser listens so the firewalled peer can connect back to it. + let browser_port = free_port().expect("browser port"); + let mut browser = Client::with_settings(server.listening_settings( + "e2e_fw_browser", + "pw", + browser_port, + )); + browser.connect().expect("browser connect"); + assert!(browser.login().expect("browser login")); + + std::thread::sleep(Duration::from_secs(1)); + + browser + .browse_user("e2e_fw_sharer") + .expect("browse request"); + + // The brokered round-trip has more hops; give it a generous deadline. + let deadline = Instant::now() + Duration::from_secs(30); + let mut listing = None; + while Instant::now() < deadline { + if let Some(result) = browser.take_browse_result("e2e_fw_sharer") { + listing = Some(result); + break; + } + std::thread::sleep(Duration::from_millis(200)); + } + let directories = + listing.expect("browser should receive the firewalled peer's listing"); + assert!( + directories + .iter() + .any(|d| d.files.iter().any(|(name, _)| name == "hidden.flac")), + "the brokered listing should include the shared file" + ); + + let _ = std::fs::remove_dir_all(share_dir); +}