fix(lib): serve uploads to downloaders we cannot dial
An accepted upload only ever dialled the downloader at the address the
server reported. Most downloaders sit behind a NAT, so the dial timed out
after 20s ("serve …: connection timed out") or, with port 0, the upload
sat in pending_serves forever. There was no fallback: an inbound
PierceFirewall was always treated as a P control connection.
Do what the reference clients do. When an upload starts, ask the server to
have the downloader connect back (ConnectToPeer type F) and dial them at
the same time; the first socket to arrive claims the transfer. Over the
pierced socket only the bare 4-byte transfer token precedes the offset
exchange. CantConnectToPeer and the brokered-connect timeout fail the
upload and free its slot; port 0 skips the dial instead of parking the job.
Covered by an e2e test where the leecher runs without a listener.
Closes #102
This commit is contained in:
@@ -114,6 +114,24 @@ struct UploadJob {
|
||||
offered: Option<Instant>,
|
||||
}
|
||||
|
||||
/// An upload waiting for its downloader to connect back to our listener while
|
||||
/// we also dial them. Whichever socket arrives first claims the transfer.
|
||||
pub(crate) struct BrokeredUpload {
|
||||
pub(crate) token: u32,
|
||||
pub(crate) real_path: std::path::PathBuf,
|
||||
deadline: Instant,
|
||||
claimed: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl BrokeredUpload {
|
||||
/// Take the transfer for this route; false if another route already did.
|
||||
pub(crate) fn claim(&self) -> bool {
|
||||
self.claimed
|
||||
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Live bookkeeping for an upload being served (or recently finished).
|
||||
struct ActiveUpload {
|
||||
username: String,
|
||||
@@ -504,6 +522,10 @@ pub struct ClientContext {
|
||||
active_uploads: HashMap<u32, ActiveUpload>,
|
||||
/// Upload tokens waiting for the downloader's address to be resolved.
|
||||
pending_serves: HashMap<String, Vec<u32>>,
|
||||
/// Uploads whose downloader we could not dial, keyed by the correlation
|
||||
/// token we sent in `ConnectToPeer(F)`; the peer quotes it back in a
|
||||
/// `PierceFirewall` and we serve over that socket instead.
|
||||
brokered_uploads: HashMap<u32, BrokeredUpload>,
|
||||
/// Shared-file listings received from peers we browsed.
|
||||
browse_results: HashMap<String, Vec<SharedDirectory>>,
|
||||
/// One-folder listings received from peers, keyed by peer and folder.
|
||||
|
||||
Reference in New Issue
Block a user