1use super::*;
2
3pub(super) const MAX_ACTIVE_INCOMING_RESOURCES: usize = 16;
7
8pub(super) struct ResourceSendParams<'a> {
9 pub(super) data: &'a [u8],
10 pub(super) metadata: Option<&'a [u8]>,
11 pub(super) auto_compress: bool,
12 pub(super) compressor: &'a dyn rns_core::buffer::types::Compressor,
13 pub(super) is_response: bool,
14 pub(super) request_id: Option<Vec<u8>>,
15 pub(super) rng: &'a mut dyn Rng,
16 pub(super) now: f64,
17}
18
19impl LinkManager {
20 pub(super) fn request_response_deadline(link: &ManagedLink, now: f64) -> f64 {
21 now + link.engine.rtt().unwrap_or(1.0) * constants::LINK_TRAFFIC_TIMEOUT_FACTOR
22 + constants::RESOURCE_RESPONSE_MAX_GRACE_TIME * 1.125
23 }
24
25 pub(super) fn build_resource_senders(
26 link: &ManagedLink,
27 params: ResourceSendParams<'_>,
28 ) -> Result<Vec<ResourceSender>, rns_core::resource::ResourceError> {
29 let ResourceSendParams {
30 data,
31 metadata,
32 auto_compress,
33 compressor,
34 is_response,
35 request_id,
36 rng,
37 now,
38 } = params;
39 let link_rtt = link.engine.rtt().unwrap_or(1.0);
40 let resource_sdu = Self::resource_sdu_for_link(link);
41 let metadata_overhead = metadata.map(|m| 3 + m.len()).unwrap_or(0);
42 let logical_size = metadata_overhead + data.len();
43
44 if logical_size <= constants::RESOURCE_MAX_EFFICIENT_SIZE {
45 let enc_rng = std::cell::RefCell::new(rns_crypto::OsRng);
46 let encrypt_fn = |plaintext: &[u8]| -> Vec<u8> {
47 link.engine
48 .encrypt(plaintext, &mut *enc_rng.borrow_mut())
49 .unwrap_or_else(|_| plaintext.to_vec())
50 };
51 return ResourceSender::new(
52 data,
53 metadata,
54 resource_sdu,
55 &encrypt_fn,
56 compressor,
57 rng,
58 now,
59 auto_compress,
60 is_response,
61 request_id,
62 1,
63 1,
64 None,
65 link_rtt,
66 6.0,
67 )
68 .map(|sender| vec![sender]);
69 }
70
71 if metadata_overhead > constants::RESOURCE_MAX_EFFICIENT_SIZE {
72 return Err(rns_core::resource::ResourceError::TooLarge);
73 }
74
75 let first_payload_len = core::cmp::min(
76 data.len(),
77 constants::RESOURCE_MAX_EFFICIENT_SIZE - metadata_overhead,
78 );
79 let remaining = data.len().saturating_sub(first_payload_len);
80 let total_segments = 1 + remaining.div_ceil(constants::RESOURCE_MAX_EFFICIENT_SIZE) as u64;
81
82 let enc_rng = std::cell::RefCell::new(rns_crypto::OsRng);
83 let encrypt_fn = |plaintext: &[u8]| -> Vec<u8> {
84 link.engine
85 .encrypt(plaintext, &mut *enc_rng.borrow_mut())
86 .unwrap_or_else(|_| plaintext.to_vec())
87 };
88
89 let mut senders = Vec::new();
90 let mut first = ResourceSender::new(
91 &data[..first_payload_len],
92 metadata,
93 resource_sdu,
94 &encrypt_fn,
95 compressor,
96 rng,
97 now,
98 auto_compress,
99 is_response,
100 request_id.clone(),
101 1,
102 total_segments,
103 None,
104 link_rtt,
105 6.0,
106 )?;
107 first.data_size = logical_size;
108 let original_hash = first.original_hash;
109 let has_metadata = metadata.is_some();
110 senders.push(first);
111
112 let mut offset = first_payload_len;
113 let mut segment_index = 2;
114 while offset < data.len() {
115 let end = core::cmp::min(offset + constants::RESOURCE_MAX_EFFICIENT_SIZE, data.len());
116 let mut sender = ResourceSender::new(
117 &data[offset..end],
118 None,
119 resource_sdu,
120 &encrypt_fn,
121 compressor,
122 rng,
123 now,
124 auto_compress,
125 is_response,
126 request_id.clone(),
127 segment_index,
128 total_segments,
129 Some(original_hash),
130 link_rtt,
131 6.0,
132 )?;
133 sender.data_size = logical_size;
134 sender.flags.has_metadata = has_metadata;
135 senders.push(sender);
136 segment_index += 1;
137 offset = end;
138 }
139
140 Ok(senders)
141 }
142
143 fn read_stream_segment(
144 stream: &mut OutgoingStreamTransfer,
145 capacity: usize,
146 ) -> Result<Vec<u8>, ResourceTransferError> {
147 let length = stream.remaining.min(capacity as u64) as usize;
148 let mut data = vec![0; length];
149 stream
150 .reader
151 .read_exact(&mut data)
152 .map_err(|error| ResourceTransferError::Source(error.to_string()))?;
153 stream.remaining -= length as u64;
154 if stream.remaining == 0 {
155 let mut trailing = [0u8; 1];
156 if stream
157 .reader
158 .read(&mut trailing)
159 .map_err(|error| ResourceTransferError::Source(error.to_string()))?
160 != 0
161 {
162 return Err(ResourceTransferError::Source(
163 "resource source exceeds declared length".into(),
164 ));
165 }
166 }
167 Ok(data)
168 }
169
170 #[allow(clippy::too_many_arguments)]
171 fn build_stream_segment(
172 link: &ManagedLink,
173 stream: &mut OutgoingStreamTransfer,
174 metadata: Option<&[u8]>,
175 segment_index: u64,
176 total_segments: u64,
177 original_hash: Option<[u8; 32]>,
178 rng: &mut dyn Rng,
179 now: f64,
180 ) -> Result<ResourceSender, ResourceTransferError> {
181 let metadata_overhead = metadata.map(|value| 3 + value.len()).unwrap_or(0);
182 if metadata_overhead > constants::RESOURCE_MAX_EFFICIENT_SIZE {
183 return Err(ResourceTransferError::Protocol(
184 "resource metadata exceeds maximum segment size".into(),
185 ));
186 }
187 let data = Self::read_stream_segment(
188 stream,
189 constants::RESOURCE_MAX_EFFICIENT_SIZE - metadata_overhead,
190 )?;
191 let enc_rng = std::cell::RefCell::new(rns_crypto::OsRng);
192 let encrypt_fn = |plaintext: &[u8]| -> Vec<u8> {
193 link.engine
194 .encrypt(plaintext, &mut *enc_rng.borrow_mut())
195 .unwrap_or_else(|_| plaintext.to_vec())
196 };
197 let mut sender = ResourceSender::new(
198 &data,
199 metadata,
200 Self::resource_sdu_for_link(link),
201 &encrypt_fn,
202 &Bzip2Compressor,
203 rng,
204 now,
205 stream.auto_compress,
206 false,
207 None,
208 segment_index,
209 total_segments,
210 original_hash,
211 link.engine.rtt().unwrap_or(1.0),
212 6.0,
213 )
214 .map_err(|error| ResourceTransferError::Protocol(error.to_string()))?;
215 sender.data_size = (stream.declared_length + stream.metadata_overhead) as usize;
216 sender.flags.has_metadata = stream.metadata_overhead != 0;
217 Ok(sender)
218 }
219
220 pub(super) fn start_resource_senders(
221 link: &mut ManagedLink,
222 mut senders: Vec<ResourceSender>,
223 now: f64,
224 ) -> Vec<ResourceAction> {
225 if senders.is_empty() {
226 return Vec::new();
227 }
228
229 let mut first = senders.remove(0);
230 let adv_actions = first.advertise(now);
231
232 if first.total_segments > 1 {
233 let original_hash = first.original_hash;
234 let split = OutgoingSplitTransfer {
235 total_segments: first.total_segments,
236 completed_segments: 0,
237 current_segment_index: first.segment_index,
238 current_sent_parts: 0,
239 current_total_parts: first.total_parts(),
240 };
241 link.outgoing_splits.insert(original_hash, split);
242 }
243
244 link.outgoing_resources.push(first);
245 link.outgoing_resources.extend(senders);
246 adv_actions
247 }
248
249 pub(super) fn handle_resource_adv(
251 &mut self,
252 link_id: &LinkId,
253 adv_plaintext: &[u8],
254 rng: &mut dyn Rng,
255 ) -> Vec<LinkManagerAction> {
256 let has_request_handlers = !self.request_handlers.is_empty()
257 || !self.deferred_request_handlers.is_empty()
258 || !self.management_paths.is_empty();
259 let link = match self.links.get_mut(link_id) {
260 Some(l) => l,
261 None => return Vec::new(),
262 };
263
264 let link_rtt = link.engine.rtt().unwrap_or(1.0);
265 let resource_sdu = Self::resource_sdu_for_link(link);
266 let now = time::now();
267
268 let receiver = match ResourceReceiver::from_advertisement(
269 adv_plaintext,
270 resource_sdu,
271 link_rtt,
272 now,
273 None,
274 None,
275 ) {
276 Ok(r) => r,
277 Err(e) => {
278 log::debug!("Resource ADV rejected: {}", e);
279 let _ = link;
280 return self.teardown_link(link_id);
281 }
282 };
283
284 if link
289 .incoming_resources
290 .iter()
291 .any(|existing| existing.resource_hash == receiver.resource_hash)
292 {
293 return Vec::new();
294 }
295
296 let active_incoming = link
297 .incoming_resources
298 .iter()
299 .filter(|existing| existing.status < rns_core::resource::ResourceStatus::Complete)
300 .count();
301 if active_incoming >= MAX_ACTIVE_INCOMING_RESOURCES {
302 log::debug!(
303 "refused resource advertisement on link {:02x?}: {} transfers in progress",
304 &link_id[..4],
305 active_incoming
306 );
307 let reject_actions = {
308 let mut receiver = receiver;
309 receiver.reject()
310 };
311 let _ = link;
312 return self.process_resource_actions(link_id, reject_actions, rng);
313 }
314
315 let strategy = link.resource_strategy;
316 let resource_hash = receiver.resource_hash.clone();
317 let transfer_size = receiver.transfer_size;
318 let has_metadata = receiver.has_metadata;
319 let is_request = receiver.flags.is_request;
320 let is_response = receiver.flags.is_response;
321 let is_split = receiver.flags.split;
322 let segment_index = receiver.segment_index;
323 let total_segments = receiver.total_segments;
324 let original_hash = match Self::resource_hash_key(&receiver.original_hash) {
325 Some(key) => key,
326 None => return Vec::new(),
327 };
328
329 if !is_request && !is_response {
330 let maximum = match &link.resource_receive_mode {
331 ResourceReceiveMode::Memory { max_bytes } => Some(*max_bytes),
332 ResourceReceiveMode::TemporaryFile { max_bytes, .. } => *max_bytes,
333 };
334 if maximum.is_some_and(|maximum| receiver.data_size > maximum) {
335 let reject_actions = {
336 let mut receiver = receiver;
337 receiver.reject()
338 };
339 let _ = link;
340 return self.process_resource_actions(link_id, reject_actions, rng);
341 }
342 }
343
344 if is_split && segment_index > 1 {
345 let should_accept = link
346 .incoming_splits
347 .get(&original_hash)
348 .is_some_and(|split| {
349 split.completed_segments + 1 == segment_index
350 && split.total_segments == total_segments
351 });
352
353 if !should_accept {
354 let reject_actions = {
355 let mut r = receiver;
356 r.reject()
357 };
358 let _ = link;
359 return self.process_resource_actions(link_id, reject_actions, rng);
360 }
361
362 let current_total_parts = receiver.total_parts;
363 link.incoming_resources.push(receiver);
364 let idx = link.incoming_resources.len() - 1;
365 if let Some(split) = link.incoming_splits.get_mut(&original_hash) {
366 split.current_segment_index = segment_index;
367 split.current_received_parts = 0;
368 split.current_total_parts = current_total_parts;
369 }
370 let resource_actions = link.incoming_resources[idx].accept(now);
371 let _ = link;
372 return self.process_resource_actions(link_id, resource_actions, rng);
373 }
374
375 if is_request {
376 if !has_request_handlers || Self::response_request_id(&receiver.request_id).is_none() {
379 return Vec::new();
380 }
381 if link
382 .max_request_size
383 .is_some_and(|limit| receiver.data_size > limit as u64)
384 {
385 log::debug!(
386 "ignored request with excessive size {} bytes on link {:02x?}",
387 receiver.data_size,
388 &link_id[..4]
389 );
390 let reject_actions = {
391 let mut receiver = receiver;
392 receiver.reject()
393 };
394 let _ = link;
395 return self.process_resource_actions(link_id, reject_actions, rng);
396 }
397 if is_split {
398 link.incoming_splits.insert(
399 original_hash,
400 IncomingSplitTransfer {
401 total_segments,
402 completed_segments: 0,
403 current_segment_index: segment_index,
404 current_received_parts: 0,
405 current_total_parts: receiver.total_parts,
406 storage: IncomingSplitStorage::Memory(Vec::new()),
407 metadata: None,
408 is_request: true,
409 is_response: false,
410 },
411 );
412 }
413 link.incoming_resources.push(receiver);
414 let idx = link.incoming_resources.len() - 1;
415 let resource_actions = link.incoming_resources[idx].accept(now);
416 let _ = link;
417 return self.process_resource_actions(link_id, resource_actions, rng);
418 }
419
420 if is_response {
421 let Some(request_id) = Self::response_request_id(&receiver.request_id) else {
424 return Vec::new();
425 };
426 let Some(pending) = link.pending_requests.get(&request_id) else {
427 return Vec::new();
428 };
429 if let Some(maximum) = pending.max_response_size {
430 if receiver.data_size > maximum as u64 {
431 let response_size = receiver.data_size;
432 link.pending_requests.remove(&request_id);
433 log::debug!(
434 "rejected response with excessive size {} bytes on link {:02x?}",
435 response_size,
436 &link_id[..4]
437 );
438 let reject_actions = {
439 let mut receiver = receiver;
440 receiver.reject()
441 };
442 let _ = link;
443 let mut actions = self.process_resource_actions(link_id, reject_actions, rng);
444 actions.push(LinkManagerAction::RequestFailed {
445 link_id: *link_id,
446 request_id,
447 reason: RequestFailure::ResponseTooLarge {
448 size: response_size,
449 maximum,
450 },
451 });
452 return actions;
453 }
454 }
455 if is_split {
456 link.incoming_splits.insert(
457 original_hash,
458 IncomingSplitTransfer {
459 total_segments,
460 completed_segments: 0,
461 current_segment_index: segment_index,
462 current_received_parts: 0,
463 current_total_parts: receiver.total_parts,
464 storage: IncomingSplitStorage::Memory(Vec::new()),
465 metadata: None,
466 is_request: false,
467 is_response,
468 },
469 );
470 }
471 link.incoming_resources.push(receiver);
472 let idx = link.incoming_resources.len() - 1;
473 let resource_actions = link.incoming_resources[idx].accept(now);
474 let _ = link;
475 return self.process_resource_actions(link_id, resource_actions, rng);
476 }
477
478 match strategy {
479 ResourceStrategy::AcceptNone => {
480 let reject_actions = {
482 let mut r = receiver;
483 r.reject()
484 };
485 self.process_resource_actions(link_id, reject_actions, rng)
486 }
487 ResourceStrategy::AcceptAll => {
488 if is_split {
489 let storage =
490 match Self::incoming_storage(&link.resource_receive_mode, &original_hash) {
491 Ok(storage) => storage,
492 Err(error) => {
493 let reject_actions = {
494 let mut receiver = receiver;
495 receiver.reject()
496 };
497 let _ = link;
498 let mut actions =
499 self.process_resource_actions(link_id, reject_actions, rng);
500 actions.push(LinkManagerAction::ResourceFailed {
501 link_id: *link_id,
502 error: format!("resource storage failed: {error}"),
503 });
504 return actions;
505 }
506 };
507 link.incoming_splits.insert(
508 original_hash,
509 IncomingSplitTransfer {
510 total_segments,
511 completed_segments: 0,
512 current_segment_index: segment_index,
513 current_received_parts: 0,
514 current_total_parts: receiver.total_parts,
515 storage,
516 metadata: None,
517 is_request: false,
518 is_response,
519 },
520 );
521 }
522 link.incoming_resources.push(receiver);
523 let idx = link.incoming_resources.len() - 1;
524 let resource_actions = link.incoming_resources[idx].accept(now);
525 let _ = link;
526 self.process_resource_actions(link_id, resource_actions, rng)
527 }
528 ResourceStrategy::AcceptApp => {
529 link.incoming_resources.push(receiver);
530 vec![LinkManagerAction::ResourceAcceptQuery {
532 link_id: *link_id,
533 resource_hash,
534 transfer_size,
535 has_metadata,
536 }]
537 }
538 }
539 }
540
541 pub fn accept_resource(
543 &mut self,
544 link_id: &LinkId,
545 resource_hash: &[u8],
546 accept: bool,
547 rng: &mut dyn Rng,
548 ) -> Vec<LinkManagerAction> {
549 let link = match self.links.get_mut(link_id) {
550 Some(l) => l,
551 None => return Vec::new(),
552 };
553
554 let now = time::now();
555 let idx = link
556 .incoming_resources
557 .iter()
558 .position(|r| r.resource_hash == resource_hash);
559 let idx = match idx {
560 Some(i) => i,
561 None => return Vec::new(),
562 };
563
564 if accept && link.incoming_resources[idx].flags.split {
565 if let Some(original_hash) =
566 Self::resource_hash_key(&link.incoming_resources[idx].original_hash)
567 {
568 if !link.incoming_splits.contains_key(&original_hash) {
569 let storage =
570 match Self::incoming_storage(&link.resource_receive_mode, &original_hash) {
571 Ok(storage) => storage,
572 Err(error) => {
573 let resource_actions = link.incoming_resources[idx].reject();
574 let _ = link;
575 let mut actions =
576 self.process_resource_actions(link_id, resource_actions, rng);
577 actions.push(LinkManagerAction::ResourceFailed {
578 link_id: *link_id,
579 error: format!("resource storage failed: {error}"),
580 });
581 return actions;
582 }
583 };
584 link.incoming_splits.insert(
585 original_hash,
586 IncomingSplitTransfer {
587 total_segments: link.incoming_resources[idx].total_segments,
588 completed_segments: 0,
589 current_segment_index: link.incoming_resources[idx].segment_index,
590 current_received_parts: 0,
591 current_total_parts: link.incoming_resources[idx].total_parts,
592 storage,
593 metadata: None,
594 is_request: link.incoming_resources[idx].flags.is_request,
595 is_response: link.incoming_resources[idx].flags.is_response,
596 },
597 );
598 }
599 }
600 }
601
602 let resource_actions = if accept {
603 link.incoming_resources[idx].accept(now)
604 } else {
605 link.incoming_resources[idx].reject()
606 };
607
608 let _ = link;
609 self.process_resource_actions(link_id, resource_actions, rng)
610 }
611
612 pub(super) fn handle_resource_req(
614 &mut self,
615 link_id: &LinkId,
616 plaintext: &[u8],
617 rng: &mut dyn Rng,
618 ) -> Vec<LinkManagerAction> {
619 let link = match self.links.get_mut(link_id) {
620 Some(l) => l,
621 None => return Vec::new(),
622 };
623
624 let now = time::now();
625 let mut all_actions = Vec::new();
626 let mut progress_update = None;
627 let mut progress_stream_hash = None;
628 for sender in &mut link.outgoing_resources {
629 if sender.flags.split && sender.status == rns_core::resource::ResourceStatus::Queued {
630 continue;
631 }
632 let before_sent = sender.sent_parts;
633 let resource_actions = sender.handle_request(plaintext, now);
634 if !resource_actions.is_empty() {
635 if sender.sent_parts != before_sent {
636 progress_stream_hash = Some(sender.original_hash);
637 if sender.flags.split {
638 if let Some(split) = link.outgoing_splits.get_mut(&sender.original_hash) {
639 split.current_segment_index = sender.segment_index;
640 split.current_sent_parts = sender.sent_parts;
641 split.current_total_parts = sender.total_parts();
642 progress_update =
643 Some(Self::outgoing_split_progress(split, sender.sdu));
644 }
645 } else {
646 progress_update = Some((sender.sent_parts, sender.total_parts()));
647 }
648 }
649 all_actions.extend(resource_actions);
650 break;
651 }
652 }
653
654 let _ = link;
655 let mut out = self.process_resource_actions(link_id, all_actions, rng);
656 if let Some((received, total)) = progress_update {
657 if let Some((stream, transferred)) = progress_stream_hash.and_then(|hash| {
658 self.links.get(link_id).and_then(|link| {
659 link.outgoing_streams.get(&hash).map(|stream| {
660 let transferred = if total == 0 {
661 0
662 } else {
663 stream.declared_length.saturating_mul(received as u64) / total as u64
664 };
665 (stream, transferred.min(stream.declared_length))
666 })
667 })
668 }) {
669 out.push(LinkManagerAction::ResourceStreamProgress {
670 link_id: *link_id,
671 transfer_id: stream.transfer_id,
672 transferred,
673 total: stream.declared_length,
674 });
675 } else {
676 out.push(LinkManagerAction::ResourceProgress {
677 link_id: *link_id,
678 received,
679 total,
680 });
681 }
682 }
683 out
684 }
685
686 pub(super) fn handle_resource_hmu(
688 &mut self,
689 link_id: &LinkId,
690 plaintext: &[u8],
691 rng: &mut dyn Rng,
692 ) -> Vec<LinkManagerAction> {
693 let link = match self.links.get_mut(link_id) {
694 Some(l) => l,
695 None => return Vec::new(),
696 };
697
698 let now = time::now();
699 let mut all_actions = Vec::new();
700 for receiver in &mut link.incoming_resources {
701 let resource_actions = receiver.handle_hashmap_update(plaintext, now);
702 if !resource_actions.is_empty() {
703 all_actions.extend(resource_actions);
704 break;
705 }
706 }
707
708 let _ = link;
709 self.process_resource_actions(link_id, all_actions, rng)
710 }
711
712 pub(super) fn handle_resource_part(
714 &mut self,
715 link_id: &LinkId,
716 raw_data: &[u8],
717 rng: &mut dyn Rng,
718 ) -> Vec<LinkManagerAction> {
719 let link = match self.links.get_mut(link_id) {
720 Some(l) => l,
721 None => return Vec::new(),
722 };
723
724 let now = time::now();
725 let resource_sdu = Self::resource_sdu_for_link(link);
726 let mut all_actions = Vec::new();
727 let mut assemble_idx = None;
728 let mut assembled_is_request = false;
729 let mut assembled_is_response = false;
730 let mut request_request_id = None;
731 let mut response_request_id = None;
732 let mut completed_file = None;
733 let mut storage_failure = None;
734
735 for (idx, receiver) in link.incoming_resources.iter_mut().enumerate() {
736 if receiver.status >= rns_core::resource::ResourceStatus::Assembling {
737 continue;
738 }
739 let resource_actions = receiver.receive_part(raw_data, now);
740 if !resource_actions.is_empty() {
741 if receiver.received_count == receiver.total_parts {
742 assemble_idx = Some(idx);
743 }
744 if receiver.flags.split {
745 if let Some(key) = Self::resource_hash_key(&receiver.original_hash) {
746 if let Some(split) = link.incoming_splits.get_mut(&key) {
747 split.current_segment_index = receiver.segment_index;
748 split.current_received_parts = receiver.received_count;
749 split.current_total_parts = receiver.total_parts;
750 let (received, total) =
751 Self::incoming_split_progress(split, resource_sdu);
752 for action in resource_actions {
753 match action {
754 ResourceAction::ProgressUpdate { .. } => {
755 all_actions.push(ResourceAction::ProgressUpdate {
756 received,
757 total,
758 });
759 }
760 other => all_actions.push(other),
761 }
762 }
763 } else {
764 all_actions.extend(resource_actions);
765 }
766 } else {
767 all_actions.extend(resource_actions);
768 }
769 } else {
770 all_actions.extend(resource_actions);
771 }
772 break;
773 }
774 }
775
776 let mut preceding = Vec::new();
777 if let Some(idx) = assemble_idx {
778 let eligible = Self::receive_offload_eligible(link, &link.incoming_resources[idx]);
779 let _ = link;
780 preceding = self.wait_receive_for(link_id, rng);
781 if eligible {
782 self.ensure_receive_worker();
783 }
784 let Some(link) = self
787 .links
788 .get_mut(link_id)
789 .filter(|link| link.engine.state() == LinkState::Active)
790 else {
791 return preceding;
792 };
793 if idx >= link.incoming_resources.len() {
794 return preceding;
795 }
796 if eligible && self.receive_worker.is_some() {
797 let receiver = &mut link.incoming_resources[idx];
798 let actions = match receiver
799 .prepare_assembly(&|data| link.engine.decrypt(data).map_err(|_| ()))
800 {
801 Ok(assembly) => match self.receive_worker.as_mut().unwrap().submit(
802 *link_id,
803 link.resource_generation.clone(),
804 assembly,
805 ) {
806 Ok(()) => Vec::new(),
807 Err(assembly) => receiver.complete_assembly(assembly.run(&Bzip2Compressor)),
810 },
811 Err(actions) => actions,
812 };
813 all_actions.extend(actions);
814 let _ = link;
815 preceding.extend(self.process_resource_actions(link_id, all_actions, rng));
816 return preceding;
817 }
818 let split_key = if link.incoming_resources[idx].flags.split {
819 Self::resource_hash_key(&link.incoming_resources[idx].original_hash)
820 } else {
821 None
822 };
823 let split_segment_index = link.incoming_resources[idx].segment_index;
824 let split_segment_total = link.incoming_resources[idx].total_segments;
825 let split_segment_parts = link.incoming_resources[idx].total_parts;
826 let split_is_request = link.incoming_resources[idx].flags.is_request;
827 let split_is_response = link.incoming_resources[idx].flags.is_response;
828 let resource_original_hash =
829 Self::resource_hash_key(&link.incoming_resources[idx].original_hash)
830 .unwrap_or([0; 32]);
831 let resource_request_id =
832 Self::response_request_id(&link.incoming_resources[idx].request_id);
833 if split_is_request {
834 request_request_id = resource_request_id;
835 } else if split_is_response {
836 response_request_id = resource_request_id;
837 }
838 let decrypt_fn = |ciphertext: &[u8]| -> Result<Vec<u8>, ()> {
839 link.engine.decrypt(ciphertext).map_err(|_| ())
840 };
841 let mut assemble_actions =
842 link.incoming_resources[idx].assemble(&decrypt_fn, &Bzip2Compressor);
843 assembled_is_request = split_is_request;
844 assembled_is_response = split_is_response;
845
846 if let Some(key) = split_key {
847 let mut converted_actions = Vec::new();
848 let mut segment_data = None;
849 let mut segment_metadata = None;
850 for action in assemble_actions {
851 match action {
852 ResourceAction::DataReceived { data, metadata } => {
853 segment_data = Some(data);
854 segment_metadata = metadata;
855 }
856 ResourceAction::Completed => {}
857 other => converted_actions.push(other),
858 }
859 }
860
861 if let Some(data) = segment_data {
862 if let Some(split) = link.incoming_splits.get_mut(&key) {
863 let write_result = match &mut split.storage {
864 IncomingSplitStorage::Memory(buffer) => {
865 buffer.extend_from_slice(&data);
866 Ok(())
867 }
868 IncomingSplitStorage::File { file, size, .. } => {
869 file.write_all(&data).map(|()| *size += data.len() as u64)
870 }
871 };
872 if let Err(error) = write_result {
873 storage_failure = Some(error.to_string());
874 }
875 if segment_metadata.is_some() {
876 split.metadata = segment_metadata;
877 }
878 split.completed_segments = split_segment_index;
879 split.current_segment_index = split_segment_index;
880 split.current_received_parts = split_segment_parts;
881 split.current_total_parts = split_segment_parts;
882 }
883
884 if storage_failure.is_none() && split_segment_index == split_segment_total {
885 if let Some(split) = link.incoming_splits.remove(&key) {
886 assembled_is_request = split.is_request;
887 assembled_is_response = split.is_response;
888 match split.storage {
889 IncomingSplitStorage::Memory(data) => {
890 converted_actions.push(ResourceAction::DataReceived {
891 data,
892 metadata: split.metadata,
893 });
894 }
895 IncomingSplitStorage::File { file, path, size } => {
896 match file.sync_all() {
897 Ok(()) => {
898 completed_file = Some(ReceivedResourceFile::new(
899 path,
900 key,
901 size,
902 split.metadata,
903 ));
904 }
905 Err(error) => storage_failure = Some(error.to_string()),
906 }
907 }
908 }
909 converted_actions.push(ResourceAction::Completed);
910 }
911 }
912 if storage_failure.is_some() {
913 link.incoming_splits.remove(&key);
914 converted_actions.clear();
915 converted_actions.push(ResourceAction::SendCancelReceiver(
916 link.incoming_resources[idx].resource_hash.clone(),
917 ));
918 }
919 }
920
921 assemble_actions = converted_actions;
922 } else if !split_is_request
923 && !split_is_response
924 && matches!(
925 link.resource_receive_mode,
926 ResourceReceiveMode::TemporaryFile { .. }
927 )
928 {
929 let directory = match &link.resource_receive_mode {
930 ResourceReceiveMode::TemporaryFile { directory, .. } => directory.clone(),
931 ResourceReceiveMode::Memory { .. } => unreachable!(),
932 };
933 let mut converted_actions = Vec::new();
934 for action in assemble_actions {
935 match action {
936 ResourceAction::DataReceived { data, metadata } => {
937 match crate::resource::create_receive_file(
938 &directory,
939 &resource_original_hash,
940 )
941 .and_then(|(mut file, path)| {
942 file.write_all(&data)?;
943 file.sync_all()?;
944 Ok(ReceivedResourceFile::new(
945 path,
946 resource_original_hash,
947 data.len() as u64,
948 metadata,
949 ))
950 }) {
951 Ok(resource) => completed_file = Some(resource),
952 Err(error) => storage_failure = Some(error.to_string()),
953 }
954 }
955 other => converted_actions.push(other),
956 }
957 }
958 if storage_failure.is_some() {
959 converted_actions.retain(|action| {
960 !matches!(
961 action,
962 ResourceAction::SendProof(_) | ResourceAction::Completed
963 )
964 });
965 converted_actions.push(ResourceAction::SendCancelReceiver(
966 link.incoming_resources[idx].resource_hash.clone(),
967 ));
968 }
969 assemble_actions = converted_actions;
970 }
971 all_actions.extend(assemble_actions);
972 }
973
974 let mut out = self.process_resource_actions(link_id, all_actions, rng);
975 if let Some(resource) = completed_file {
976 out.push(LinkManagerAction::ResourceFileReceived {
977 link_id: *link_id,
978 resource,
979 });
980 }
981 if let Some(error) = storage_failure {
982 out.push(LinkManagerAction::ResourceFailed {
983 link_id: *link_id,
984 error: format!("resource storage failed: {error}"),
985 });
986 }
987
988 if assembled_is_request {
989 let mut converted = Vec::new();
990 for action in out {
991 match action {
992 LinkManagerAction::ResourceReceived { data, .. } => {
993 if let Some(request_id) = request_request_id {
994 converted.extend(self.handle_request(link_id, &data, request_id, rng));
995 }
996 }
997 LinkManagerAction::ResourceAcceptQuery { .. } => {
998 }
1000 other => converted.push(other),
1001 }
1002 }
1003 out = converted;
1004 } else if assembled_is_response {
1005 let mut converted = Vec::new();
1006 for action in out {
1007 match action {
1008 LinkManagerAction::ResourceReceived { data, metadata, .. } => {
1009 converted.extend(self.handle_response(
1010 link_id,
1011 &data,
1012 metadata,
1013 response_request_id,
1014 ));
1015 }
1016 LinkManagerAction::ResourceAcceptQuery { .. } => {
1017 }
1019 other => converted.push(other),
1020 }
1021 }
1022 out = converted;
1023 }
1024
1025 preceding.extend(out);
1028 preceding
1029 }
1030
1031 pub(super) fn handle_resource_prf(
1033 &mut self,
1034 link_id: &LinkId,
1035 plaintext: &[u8],
1036 rng: &mut dyn Rng,
1037 ) -> Vec<LinkManagerAction> {
1038 let link = match self.links.get_mut(link_id) {
1039 Some(l) => l,
1040 None => return Vec::new(),
1041 };
1042
1043 let now = time::now();
1044 let mut result_actions = Vec::new();
1045 let mut completed_sender = None;
1046 let mut completed_request_id = None;
1047 let mut failed_split = None;
1048 let mut failed_request_id = None;
1049 let proof_hash = plaintext.get(..32);
1050 for sender in &mut link.outgoing_resources {
1051 if proof_hash.is_some_and(|hash| hash != sender.resource_hash.as_slice()) {
1052 continue;
1053 }
1054 let resource_actions = sender.handle_proof(plaintext, now);
1055 if !resource_actions.is_empty() {
1056 if resource_actions
1057 .iter()
1058 .any(|action| matches!(action, ResourceAction::Completed))
1059 {
1060 completed_sender = Some((
1061 sender.original_hash,
1062 sender.segment_index,
1063 sender.total_segments,
1064 sender.total_parts(),
1065 ));
1066 if sender.flags.is_request && sender.segment_index == sender.total_segments {
1067 completed_request_id = Self::response_request_id(&sender.request_id);
1068 }
1069 }
1070 if resource_actions
1071 .iter()
1072 .any(|action| matches!(action, ResourceAction::Failed(_)))
1073 {
1074 failed_split = Some(sender.original_hash);
1075 }
1076 if resource_actions
1077 .iter()
1078 .any(|action| matches!(action, ResourceAction::Failed(_)))
1079 && sender.flags.is_request
1080 {
1081 failed_request_id = Self::response_request_id(&sender.request_id);
1082 }
1083 result_actions.extend(resource_actions);
1084 break;
1085 }
1086 }
1087
1088 let mut actions = Vec::new();
1090 let mut advertise_next = None;
1091 let mut stream_failure = None;
1092 for ra in result_actions {
1093 match ra {
1094 ResourceAction::Completed => {
1095 if let Some((original_hash, segment_index, total_segments, total_parts)) =
1096 completed_sender
1097 {
1098 if total_segments > 1 && segment_index < total_segments {
1099 if let Some(split) = link.outgoing_splits.get_mut(&original_hash) {
1100 split.completed_segments = segment_index;
1101 split.current_segment_index = segment_index;
1102 split.current_sent_parts = total_parts;
1103 split.current_total_parts = total_parts;
1104 }
1105 if let Some(next) = link.outgoing_resources.iter_mut().find(|s| {
1106 s.flags.split
1107 && s.original_hash == original_hash
1108 && s.segment_index == segment_index + 1
1109 }) {
1110 if let Some(split) = link.outgoing_splits.get_mut(&original_hash) {
1111 split.current_segment_index = next.segment_index;
1112 split.current_sent_parts = 0;
1113 split.current_total_parts = next.total_parts();
1114 }
1115 advertise_next = Some(next.advertise(now));
1116 } else if let Some(mut stream) =
1117 link.outgoing_streams.remove(&original_hash)
1118 {
1119 match Self::build_stream_segment(
1120 link,
1121 &mut stream,
1122 None,
1123 segment_index + 1,
1124 total_segments,
1125 Some(original_hash),
1126 rng,
1127 now,
1128 ) {
1129 Ok(mut next) => {
1130 if let Some(split) =
1131 link.outgoing_splits.get_mut(&original_hash)
1132 {
1133 split.current_segment_index = next.segment_index;
1134 split.current_sent_parts = 0;
1135 split.current_total_parts = next.total_parts();
1136 }
1137 advertise_next = Some(next.advertise(now));
1138 link.outgoing_resources.push(next);
1139 link.outgoing_streams.insert(original_hash, stream);
1140 }
1141 Err(error) => {
1142 stream_failure = Some((stream.transfer_id, error));
1143 link.outgoing_splits.remove(&original_hash);
1144 }
1145 }
1146 }
1147 } else {
1148 link.outgoing_splits.remove(&original_hash);
1149 if let Some(stream) = link.outgoing_streams.remove(&original_hash) {
1150 actions.push(LinkManagerAction::ResourceStreamCompleted {
1151 link_id: *link_id,
1152 transfer_id: stream.transfer_id,
1153 });
1154 } else {
1155 actions.push(LinkManagerAction::ResourceCompleted {
1156 link_id: *link_id,
1157 });
1158 }
1159 }
1160 } else {
1161 actions.push(LinkManagerAction::ResourceCompleted { link_id: *link_id });
1162 }
1163 }
1164 ResourceAction::Failed(e) => {
1165 if let Some(original_hash) = failed_split {
1166 link.outgoing_splits.remove(&original_hash);
1167 if let Some(stream) = link.outgoing_streams.remove(&original_hash) {
1168 actions.push(LinkManagerAction::ResourceStreamFailed {
1169 link_id: *link_id,
1170 transfer_id: stream.transfer_id,
1171 error: ResourceTransferError::Protocol(e.to_string()),
1172 });
1173 continue;
1174 }
1175 }
1176 actions.push(LinkManagerAction::ResourceFailed {
1177 link_id: *link_id,
1178 error: format!("{}", e),
1179 });
1180 }
1181 _ => {}
1182 }
1183 }
1184
1185 if let Some((transfer_id, error)) = stream_failure {
1186 actions.push(LinkManagerAction::ResourceStreamFailed {
1187 link_id: *link_id,
1188 transfer_id,
1189 error,
1190 });
1191 }
1192
1193 if let Some(request_id) = completed_request_id {
1194 let deadline = Self::request_response_deadline(link, now);
1195 if let Some(entry) = link.pending_requests.get_mut(&request_id) {
1196 entry.deadline = Some(deadline);
1197 }
1198 }
1199 if let Some(request_id) = failed_request_id {
1200 link.pending_requests.remove(&request_id);
1201 }
1202
1203 link.outgoing_resources
1205 .retain(|s| s.status < rns_core::resource::ResourceStatus::Complete);
1206
1207 let _ = link;
1208 if let Some(next_actions) = advertise_next {
1209 actions.extend(self.process_resource_actions(link_id, next_actions, rng));
1210 }
1211
1212 actions
1213 }
1214
1215 pub(super) fn handle_resource_icl(
1217 &mut self,
1218 link_id: &LinkId,
1219 resource_hash: &[u8],
1220 ) -> Vec<LinkManagerAction> {
1221 let Some(hash) = Self::resource_hash_key(resource_hash) else {
1222 return Vec::new();
1223 };
1224 let Some(link) = self.links.get_mut(link_id) else {
1225 return Vec::new();
1226 };
1227 let Some(idx) = link.incoming_resources.iter().position(|receiver| {
1228 receiver.resource_hash == hash
1229 && receiver.status < rns_core::resource::ResourceStatus::Complete
1230 }) else {
1231 return Vec::new();
1232 };
1233 let mut receiver = link.incoming_resources.remove(idx);
1234 if receiver.flags.split {
1235 if let Some(original) = Self::resource_hash_key(&receiver.original_hash) {
1236 link.incoming_resources
1237 .retain(|r| !r.flags.split || r.original_hash != original);
1238 if let Some(IncomingSplitTransfer {
1239 storage: IncomingSplitStorage::File { file, path, .. },
1240 ..
1241 }) = link.incoming_splits.remove(&original)
1242 {
1243 drop(file);
1244 let _ = std::fs::remove_file(path);
1245 }
1246 }
1247 }
1248 if receiver.flags.is_response {
1249 if let Some(request) = Self::response_request_id(&receiver.request_id) {
1250 link.pending_requests.remove(&request);
1251 }
1252 }
1253 receiver
1256 .handle_cancel()
1257 .into_iter()
1258 .filter_map(|action| match action {
1259 ResourceAction::Failed(error) => Some(LinkManagerAction::ResourceFailed {
1260 link_id: *link_id,
1261 error: error.to_string(),
1262 }),
1263 _ => None,
1264 })
1265 .collect()
1266 }
1267
1268 pub(super) fn handle_resource_rcl(
1270 &mut self,
1271 link_id: &LinkId,
1272 resource_hash: &[u8],
1273 ) -> Vec<LinkManagerAction> {
1274 let Some(hash) = Self::resource_hash_key(resource_hash) else {
1275 return Vec::new();
1276 };
1277 let Some(link) = self.links.get_mut(link_id) else {
1278 return Vec::new();
1279 };
1280 let Some(idx) = link.outgoing_resources.iter().position(|sender| {
1281 sender.resource_hash == hash
1282 && sender.status < rns_core::resource::ResourceStatus::Complete
1283 }) else {
1284 return Vec::new();
1285 };
1286 let mut sender = link.outgoing_resources.remove(idx);
1287 if sender.flags.split {
1288 link.outgoing_resources
1290 .retain(|s| !s.flags.split || s.original_hash != sender.original_hash);
1291 link.outgoing_splits.remove(&sender.original_hash);
1292 }
1293 if sender.flags.is_request {
1294 if let Some(request) = Self::response_request_id(&sender.request_id) {
1295 link.pending_requests.remove(&request);
1296 }
1297 }
1298 let mut actions: Vec<_> = sender
1299 .handle_reject()
1300 .into_iter()
1301 .filter_map(|action| match action {
1302 ResourceAction::Failed(error) => Some(LinkManagerAction::ResourceFailed {
1303 link_id: *link_id,
1304 error: error.to_string(),
1305 }),
1306 _ => None,
1307 })
1308 .collect();
1309 if let Some(stream) = link.outgoing_streams.remove(&sender.original_hash) {
1310 actions.push(LinkManagerAction::ResourceStreamFailed {
1311 link_id: *link_id,
1312 transfer_id: stream.transfer_id,
1313 error: ResourceTransferError::Cancelled,
1314 });
1315 }
1316 actions
1317 }
1318
1319 fn resource_link_is_active(&self, link_id: &LinkId) -> bool {
1320 self.links
1321 .get(link_id)
1322 .is_some_and(|link| link.engine.state() == LinkState::Active)
1323 }
1324
1325 fn abort_resources_on_inactive_link(&mut self, link_id: &LinkId) -> Vec<LinkManagerAction> {
1326 let Some(link) = self.links.get_mut(link_id) else {
1327 return Vec::new();
1328 };
1329 for sender in &mut link.outgoing_resources {
1330 let _ = sender.cancel();
1331 }
1332 for receiver in &mut link.incoming_resources {
1333 let _ = receiver.cancel();
1334 }
1335 link.outgoing_resources.clear();
1336 link.incoming_resources.clear();
1337 link.outgoing_splits.clear();
1338 let actions = link
1339 .outgoing_streams
1340 .drain()
1341 .map(|(_, stream)| LinkManagerAction::ResourceStreamFailed {
1342 link_id: *link_id,
1343 transfer_id: stream.transfer_id,
1344 error: ResourceTransferError::Cancelled,
1345 })
1346 .collect();
1347 link.incoming_splits.clear();
1348 link.pending_requests.clear();
1349 actions
1350 }
1351
1352 pub(super) fn process_resource_actions(
1354 &mut self,
1355 link_id: &LinkId,
1356 actions: Vec<ResourceAction>,
1357 rng: &mut dyn Rng,
1358 ) -> Vec<LinkManagerAction> {
1359 let mut result = Vec::new();
1360 for action in actions {
1361 let requires_active_link = matches!(
1362 &action,
1363 ResourceAction::SendAdvertisement(_)
1364 | ResourceAction::SendPart(_)
1365 | ResourceAction::SendRequest(_)
1366 | ResourceAction::SendHmu(_)
1367 | ResourceAction::SendProof(_)
1368 );
1369 if requires_active_link && !self.resource_link_is_active(link_id) {
1370 result.extend(self.abort_resources_on_inactive_link(link_id));
1371 continue;
1372 }
1373
1374 match action {
1375 ResourceAction::SendAdvertisement(data) => {
1376 let encrypted = self
1378 .links
1379 .get(link_id)
1380 .and_then(|link| link.engine.encrypt(&data, rng).ok());
1381 if let Some(encrypted) = encrypted {
1382 result.extend(self.build_link_packet(
1383 link_id,
1384 constants::CONTEXT_RESOURCE_ADV,
1385 &encrypted,
1386 ));
1387 }
1388 }
1389 ResourceAction::SendPart(data) => {
1390 result.extend(self.build_link_packet(
1392 link_id,
1393 constants::CONTEXT_RESOURCE,
1394 &data,
1395 ));
1396 }
1397 ResourceAction::SendRequest(data) => {
1398 let encrypted = self
1399 .links
1400 .get(link_id)
1401 .and_then(|link| link.engine.encrypt(&data, rng).ok());
1402 if let Some(encrypted) = encrypted {
1403 result.extend(self.build_link_packet(
1404 link_id,
1405 constants::CONTEXT_RESOURCE_REQ,
1406 &encrypted,
1407 ));
1408 }
1409 }
1410 ResourceAction::SendHmu(data) => {
1411 let encrypted = self
1412 .links
1413 .get(link_id)
1414 .and_then(|link| link.engine.encrypt(&data, rng).ok());
1415 if let Some(encrypted) = encrypted {
1416 result.extend(self.build_link_packet(
1417 link_id,
1418 constants::CONTEXT_RESOURCE_HMU,
1419 &encrypted,
1420 ));
1421 }
1422 }
1423 ResourceAction::SendProof(data) => {
1424 let flags = PacketFlags {
1425 header_type: constants::HEADER_1,
1426 context_flag: constants::FLAG_UNSET,
1427 transport_type: constants::TRANSPORT_BROADCAST,
1428 destination_type: constants::DESTINATION_LINK,
1429 packet_type: constants::PACKET_TYPE_PROOF,
1430 };
1431 if let Ok((raw, _packet_hash)) = RawPacket::pack_raw_with_hash(
1432 flags,
1433 0,
1434 link_id,
1435 None,
1436 constants::CONTEXT_RESOURCE_PRF,
1437 &data,
1438 ) {
1439 result.push(LinkManagerAction::SendPacket {
1440 raw,
1441 dest_type: constants::DESTINATION_LINK,
1442 attached_interface: None,
1443 });
1444 }
1445 }
1446 ResourceAction::SendCancelInitiator(data) => {
1447 let encrypted = self
1448 .links
1449 .get(link_id)
1450 .filter(|link| link.engine.state() == LinkState::Active)
1451 .and_then(|link| link.engine.encrypt(&data, rng).ok());
1452 if let Some(encrypted) = encrypted {
1453 result.extend(self.build_link_packet(
1454 link_id,
1455 constants::CONTEXT_RESOURCE_ICL,
1456 &encrypted,
1457 ));
1458 }
1459 }
1460 ResourceAction::SendCancelReceiver(data) => {
1461 let encrypted = self
1462 .links
1463 .get(link_id)
1464 .filter(|link| link.engine.state() == LinkState::Active)
1465 .and_then(|link| link.engine.encrypt(&data, rng).ok());
1466 if let Some(encrypted) = encrypted {
1467 result.extend(self.build_link_packet(
1468 link_id,
1469 constants::CONTEXT_RESOURCE_RCL,
1470 &encrypted,
1471 ));
1472 }
1473 }
1474 ResourceAction::DataReceived { data, metadata } => {
1475 result.push(LinkManagerAction::ResourceReceived {
1476 link_id: *link_id,
1477 data,
1478 metadata,
1479 });
1480 }
1481 ResourceAction::Completed => {
1482 result.push(LinkManagerAction::ResourceCompleted { link_id: *link_id });
1483 }
1484 ResourceAction::Failed(e) => {
1485 result.push(LinkManagerAction::ResourceFailed {
1486 link_id: *link_id,
1487 error: format!("{}", e),
1488 });
1489 }
1490 ResourceAction::TeardownLink => {
1491 let teardown_actions = match self.links.get_mut(link_id) {
1492 Some(link) => link.engine.handle_teardown(),
1493 None => Vec::new(),
1494 };
1495 result.extend(self.process_link_actions(link_id, &teardown_actions));
1496 }
1497 ResourceAction::ProgressUpdate { received, total } => {
1498 result.push(LinkManagerAction::ResourceProgress {
1499 link_id: *link_id,
1500 received,
1501 total,
1502 });
1503 }
1504 }
1505 }
1506 result
1507 }
1508
1509 pub(super) fn build_link_packet(
1511 &self,
1512 link_id: &LinkId,
1513 context: u8,
1514 data: &[u8],
1515 ) -> Vec<LinkManagerAction> {
1516 let flags = PacketFlags {
1517 header_type: constants::HEADER_1,
1518 context_flag: constants::FLAG_UNSET,
1519 transport_type: constants::TRANSPORT_BROADCAST,
1520 destination_type: constants::DESTINATION_LINK,
1521 packet_type: constants::PACKET_TYPE_DATA,
1522 };
1523 let mut actions = Vec::new();
1524 let max_mtu = self
1525 .links
1526 .get(link_id)
1527 .map(|l| l.engine.mtu() as usize)
1528 .unwrap_or(constants::MTU);
1529 if let Ok(raw) =
1530 RawPacket::pack_raw_with_max_mtu(flags, 0, link_id, None, context, data, max_mtu)
1531 {
1532 actions.push(LinkManagerAction::SendPacket {
1533 raw,
1534 dest_type: constants::DESTINATION_LINK,
1535 attached_interface: None,
1536 });
1537 }
1538 actions
1539 }
1540
1541 pub fn send_resource(
1543 &mut self,
1544 link_id: &LinkId,
1545 data: &[u8],
1546 metadata: Option<&[u8]>,
1547 rng: &mut dyn Rng,
1548 ) -> Vec<LinkManagerAction> {
1549 self.send_resource_with_auto_compress(link_id, data, metadata, true, rng)
1550 }
1551
1552 pub fn send_resource_with_auto_compress(
1554 &mut self,
1555 link_id: &LinkId,
1556 data: &[u8],
1557 metadata: Option<&[u8]>,
1558 auto_compress: bool,
1559 rng: &mut dyn Rng,
1560 ) -> Vec<LinkManagerAction> {
1561 self.send_resource_with_compressor(
1562 link_id,
1563 data,
1564 metadata,
1565 auto_compress,
1566 &Bzip2Compressor,
1567 rng,
1568 )
1569 }
1570
1571 pub(crate) fn send_resource_with_compressor(
1572 &mut self,
1573 link_id: &LinkId,
1574 data: &[u8],
1575 metadata: Option<&[u8]>,
1576 auto_compress: bool,
1577 compressor: &dyn rns_core::buffer::types::Compressor,
1578 rng: &mut dyn Rng,
1579 ) -> Vec<LinkManagerAction> {
1580 let link = match self.links.get_mut(link_id) {
1581 Some(l) => l,
1582 None => return Vec::new(),
1583 };
1584
1585 if link.engine.state() != LinkState::Active {
1586 return Vec::new();
1587 }
1588
1589 let now = time::now();
1590
1591 let senders = match Self::build_resource_senders(
1592 link,
1593 ResourceSendParams {
1594 data,
1595 metadata,
1596 auto_compress,
1597 compressor,
1598 is_response: false,
1599 request_id: None,
1600 rng,
1601 now,
1602 },
1603 ) {
1604 Ok(s) => s,
1605 Err(e) => {
1606 log::debug!("Failed to create ResourceSender: {}", e);
1607 return Vec::new();
1608 }
1609 };
1610
1611 let adv_actions = Self::start_resource_senders(link, senders, now);
1612
1613 let _ = link;
1614 self.process_resource_actions(link_id, adv_actions, rng)
1615 }
1616
1617 #[allow(clippy::too_many_arguments)]
1623 pub fn send_resource_stream(
1624 &mut self,
1625 link_id: &LinkId,
1626 transfer_id: ResourceTransferId,
1627 reader: Box<dyn Read + Send>,
1628 declared_length: u64,
1629 metadata: Option<Vec<u8>>,
1630 auto_compress: bool,
1631 rng: &mut dyn Rng,
1632 ) -> Vec<LinkManagerAction> {
1633 let Some(link) = self.links.get_mut(link_id) else {
1634 return vec![LinkManagerAction::ResourceStreamFailed {
1635 link_id: *link_id,
1636 transfer_id,
1637 error: ResourceTransferError::Protocol("link does not exist".into()),
1638 }];
1639 };
1640 if link.engine.state() != LinkState::Active {
1641 return vec![LinkManagerAction::ResourceStreamFailed {
1642 link_id: *link_id,
1643 transfer_id,
1644 error: ResourceTransferError::Protocol("link is not active".into()),
1645 }];
1646 }
1647
1648 let metadata_overhead = metadata.as_ref().map(|value| 3 + value.len()).unwrap_or(0);
1649 let Some(logical_size) = declared_length.checked_add(metadata_overhead as u64) else {
1650 return vec![LinkManagerAction::ResourceStreamFailed {
1651 link_id: *link_id,
1652 transfer_id,
1653 error: ResourceTransferError::Protocol("resource length overflow".into()),
1654 }];
1655 };
1656 if usize::try_from(logical_size).is_err()
1657 || metadata_overhead > constants::RESOURCE_MAX_EFFICIENT_SIZE
1658 {
1659 return vec![LinkManagerAction::ResourceStreamFailed {
1660 link_id: *link_id,
1661 transfer_id,
1662 error: ResourceTransferError::Protocol(
1663 "resource length cannot be represented by this platform".into(),
1664 ),
1665 }];
1666 }
1667
1668 let first_capacity = constants::RESOURCE_MAX_EFFICIENT_SIZE - metadata_overhead;
1669 let first_length = declared_length.min(first_capacity as u64);
1670 let remaining_after_first = declared_length - first_length;
1671 let total_segments =
1672 1 + remaining_after_first.div_ceil(constants::RESOURCE_MAX_EFFICIENT_SIZE as u64);
1673 let now = time::now();
1674 let mut stream = OutgoingStreamTransfer {
1675 transfer_id,
1676 reader,
1677 remaining: declared_length,
1678 declared_length,
1679 metadata_overhead: metadata_overhead as u64,
1680 auto_compress,
1681 };
1682 let mut first = match Self::build_stream_segment(
1683 link,
1684 &mut stream,
1685 metadata.as_deref(),
1686 1,
1687 total_segments,
1688 None,
1689 rng,
1690 now,
1691 ) {
1692 Ok(sender) => sender,
1693 Err(error) => {
1694 return vec![LinkManagerAction::ResourceStreamFailed {
1695 link_id: *link_id,
1696 transfer_id,
1697 error,
1698 }]
1699 }
1700 };
1701 let original_hash = first.original_hash;
1702 let current_total_parts = first.total_parts();
1703 let adv_actions = first.advertise(now);
1704 if total_segments > 1 {
1705 link.outgoing_splits.insert(
1706 original_hash,
1707 OutgoingSplitTransfer {
1708 total_segments,
1709 completed_segments: 0,
1710 current_segment_index: 1,
1711 current_sent_parts: 0,
1712 current_total_parts,
1713 },
1714 );
1715 }
1716 link.outgoing_streams.insert(original_hash, stream);
1717 link.outgoing_resources.push(first);
1718 let _ = link;
1719 self.process_resource_actions(link_id, adv_actions, rng)
1720 }
1721
1722 pub fn set_resource_strategy(&mut self, link_id: &LinkId, strategy: ResourceStrategy) {
1724 if let Some(link) = self.links.get_mut(link_id) {
1725 link.resource_strategy = strategy;
1726 }
1727 }
1728
1729 pub fn set_resource_receive_mode(&mut self, link_id: &LinkId, mode: ResourceReceiveMode) {
1730 if let Some(link) = self.links.get_mut(link_id) {
1731 link.resource_receive_mode = mode;
1732 }
1733 }
1734}