mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-12 05:29:57 +02:00
Peel relationships, eligibility, and secrets off the shared pool into AppDataStores adapters, split pairing service/protocol, and move the invitation Repository into its own module so open_all owns schemas. Co-authored-by: Cursor <cursoragent@cursor.com>
171 lines
6.3 KiB
Rust
171 lines
6.3 KiB
Rust
use std::{sync::Arc, time::Duration};
|
|
|
|
use serde_json::json;
|
|
|
|
use super::{filter_peer_addr_for_relay_mode, CoreInner};
|
|
use crate::{
|
|
handshake::{
|
|
DeliveryFailureReceipt, DeliveryReceipt, DeliveryReceiptResponse, HandshakeService,
|
|
},
|
|
invitation::PendingDeliveryReceipt,
|
|
ticket::parse_persisted_sender_address,
|
|
};
|
|
|
|
const DELIVERY_RECEIPT_TIMEOUT: Duration = Duration::from_secs(5);
|
|
const DELIVERY_RECEIPT_RETRY_INTERVAL: Duration = Duration::from_secs(5);
|
|
const DELIVERY_RECEIPT_MAX_RETRY_INTERVAL: Duration = Duration::from_secs(5 * 60);
|
|
|
|
impl CoreInner {
|
|
pub(super) async fn spawn_delivery_receipt_task(self: &Arc<Self>) {
|
|
let core = Arc::downgrade(self);
|
|
let task = tokio::spawn(async move {
|
|
let mut retry_interval = DELIVERY_RECEIPT_RETRY_INTERVAL;
|
|
loop {
|
|
let Some(core) = core.upgrade() else {
|
|
break;
|
|
};
|
|
let has_pending = core.deliver_pending_receipts().await;
|
|
if has_pending {
|
|
let notified = tokio::select! {
|
|
() = core.delivery_receipt_notify.notified() => true,
|
|
() = tokio::time::sleep(retry_interval) => false,
|
|
};
|
|
if notified {
|
|
retry_interval = DELIVERY_RECEIPT_RETRY_INTERVAL;
|
|
} else {
|
|
retry_interval = retry_interval
|
|
.saturating_mul(2)
|
|
.min(DELIVERY_RECEIPT_MAX_RETRY_INTERVAL);
|
|
}
|
|
} else {
|
|
core.delivery_receipt_notify.notified().await;
|
|
retry_interval = DELIVERY_RECEIPT_RETRY_INTERVAL;
|
|
}
|
|
}
|
|
});
|
|
*self.delivery_receipt_task.lock().await = Some(task);
|
|
}
|
|
|
|
async fn deliver_pending_receipts(&self) -> bool {
|
|
let receipts = match self.repository.list_pending_delivery_receipts().await {
|
|
Ok(receipts) => receipts,
|
|
Err(error) => {
|
|
tracing::warn!(%error, "failed to load pending delivery receipts");
|
|
return true;
|
|
}
|
|
};
|
|
let has_pending = !receipts.is_empty();
|
|
for receipt in receipts {
|
|
self.deliver_pending_receipt(receipt).await;
|
|
}
|
|
has_pending
|
|
}
|
|
|
|
async fn deliver_pending_receipt(&self, pending: PendingDeliveryReceipt) {
|
|
let sender_addr = match parse_persisted_sender_address(&pending.sender_blob_ticket) {
|
|
Ok(addr) => addr,
|
|
Err(error) => {
|
|
tracing::warn!(%error, request_id = %pending.request_id, "discarded invalid pending delivery receipt");
|
|
let _ = self
|
|
.repository
|
|
.delete_pending_delivery_receipt(&pending.request_id)
|
|
.await;
|
|
self.emit_transfer(
|
|
pending.local_transfer_id,
|
|
"receive",
|
|
"delivery",
|
|
"receipt-rejected",
|
|
json!({ "reason": "invalid-sender-ticket" }),
|
|
);
|
|
return;
|
|
}
|
|
};
|
|
let sender_addr = match filter_peer_addr_for_relay_mode(
|
|
&sender_addr,
|
|
self.relay_mode,
|
|
&self.custom_relay_urls,
|
|
) {
|
|
Ok(addr) => addr,
|
|
Err(error) => {
|
|
self.emit_transfer(
|
|
pending.local_transfer_id,
|
|
"receive",
|
|
"delivery",
|
|
"receipt-failed",
|
|
json!({ "reason": error.to_string() }),
|
|
);
|
|
return;
|
|
}
|
|
};
|
|
let client = HandshakeService::client(self.endpoint.clone(), sender_addr);
|
|
let request_id = pending.request_id.clone();
|
|
let response = tokio::time::timeout(DELIVERY_RECEIPT_TIMEOUT, async {
|
|
if let Some(reason) = pending.failure_reason {
|
|
client
|
|
.report_delivery_failure(DeliveryFailureReceipt {
|
|
request_id,
|
|
transfer_id: pending.sender_transfer_id,
|
|
token: pending.token,
|
|
reason,
|
|
})
|
|
.await
|
|
} else {
|
|
client
|
|
.report_delivery(DeliveryReceipt {
|
|
request_id,
|
|
transfer_id: pending.sender_transfer_id,
|
|
token: pending.token,
|
|
})
|
|
.await
|
|
}
|
|
})
|
|
.await;
|
|
match response {
|
|
Ok(Ok(DeliveryReceiptResponse::Recorded)) => {
|
|
if let Err(error) = self
|
|
.repository
|
|
.delete_pending_delivery_receipt(&pending.request_id)
|
|
.await
|
|
{
|
|
tracing::warn!(%error, request_id = %pending.request_id, "failed to clear recorded delivery receipt");
|
|
return;
|
|
}
|
|
self.emit_transfer(
|
|
pending.local_transfer_id,
|
|
"receive",
|
|
"delivery",
|
|
"receipt-recorded",
|
|
json!({ "sender_transfer_id": pending.sender_transfer_id }),
|
|
);
|
|
}
|
|
Ok(Ok(DeliveryReceiptResponse::Rejected { reason })) => {
|
|
let _ = self
|
|
.repository
|
|
.delete_pending_delivery_receipt(&pending.request_id)
|
|
.await;
|
|
self.emit_transfer(
|
|
pending.local_transfer_id,
|
|
"receive",
|
|
"delivery",
|
|
"receipt-rejected",
|
|
json!({ "reason": reason }),
|
|
);
|
|
}
|
|
Ok(Err(error)) => self.emit_transfer(
|
|
pending.local_transfer_id,
|
|
"receive",
|
|
"delivery",
|
|
"receipt-failed",
|
|
json!({ "reason": error.to_string() }),
|
|
),
|
|
Err(_) => self.emit_transfer(
|
|
pending.local_transfer_id,
|
|
"receive",
|
|
"delivery",
|
|
"receipt-failed",
|
|
json!({ "reason": "delivery receipt timed out" }),
|
|
),
|
|
}
|
|
}
|
|
}
|