use std::sync::Arc;
use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::oneshot;
use crate::engine::{
CommandId, Departure, IssuedCommand, PersistenceFlushCause, PersistenceFlushTarget,
SendRequestFailure, Settlement,
};
use crate::interfaces::{ConnectionView, InterfaceDescriptor, InterfaceId};
use crate::manifold::grant_lane::{TokioGrantConsumer, TokioGrantProducer};
use crate::routing::links::channel::byte_stream::StreamId;
use crate::routing::links::request::RequestId;
use crate::routing::links::resources::{ResourceHash, ResourceMetadata, ResourceStrategy};
use crate::routing::links::LinkId;
use crate::routing::request_handlers::{RequestPathHash, RequestPolicy};
use crate::runtime::node_introspection::NodeIntrospectionRequest;
#[cfg(feature = "runtime-metrics")]
use crate::runtime::RuntimeMetricsSnapshot;
use crate::runtime::{
ClearAnnounceQueuesOutcome, DestinationIdentityRetentionHostCommand, DropRouteOutcome,
DropRoutesViaOutcome, IdentityBlackholeHostCommand,
};
use crate::storage::TablePushError;
use crate::units::RttMillis;
use crate::wire::{DestinationHash, TransportId};
use prns_runtime::runtime::{PersistedStateSnapshot, SelfRatchetSnapshot, SelfRatchetsSnapshot};
#[allow(clippy::large_enum_variant)]
pub enum HostCommand {
Engine(IssuedCommand),
NotePersistenceFlush {
cause: PersistenceFlushCause,
target: PersistenceFlushTarget,
observed: Option<oneshot::Sender<()>>,
},
NotePersistenceFlushFailure {
cause: PersistenceFlushCause,
target: PersistenceFlushTarget,
observed: oneshot::Sender<()>,
},
AwaitedEngine {
issued: IssuedCommand,
completion: oneshot::Sender<Settlement>,
},
SendResource(SendResourceHostCommand),
SendResourceSegment(SendResourceSegmentHostCommand),
RespondAny(RespondAnyHostCommand),
RequestAny(RequestAnyHostCommand),
ProvideDecompressed(ProvideDecompressedHostCommand),
AddInterface(AddInterfaceCommand),
RemoveInterface {
id: InterfaceId,
departure: Departure,
},
DropRoute {
destination: DestinationHash,
reply: oneshot::Sender<DropRouteOutcome>,
},
DropRoutesVia {
transport: TransportId,
reply: oneshot::Sender<DropRoutesViaOutcome>,
},
ClearAnnounceQueues {
reply: oneshot::Sender<ClearAnnounceQueuesOutcome>,
},
IdentityBlackhole(IdentityBlackholeHostCommand),
DestinationIdentityRetention(DestinationIdentityRetentionHostCommand),
NodeIntrospection(NodeIntrospectionRequest),
SynthesizeTunnel {
interface: InterfaceId,
},
RegisterStreamReader {
link_id: LinkId,
stream_id: StreamId,
sink: UnboundedSender<StreamInbound>,
ready: oneshot::Sender<()>,
},
RegisterResourceSink {
link_id: LinkId,
sink: UnboundedSender<ResourceInbound>,
ready: oneshot::Sender<()>,
},
SetResourceStrategy {
destination: DestinationHash,
strategy: ResourceStrategy,
ready: oneshot::Sender<bool>,
},
RegisterRequestHandler {
destination: DestinationHash,
path_hash: RequestPathHash,
policy: RequestPolicy,
ready: oneshot::Sender<Result<(), TablePushError>>,
},
UnregisterRequestHandler {
destination: DestinationHash,
path_hash: RequestPathHash,
ready: oneshot::Sender<bool>,
},
SnapshotPersistedState {
reply: oneshot::Sender<PersistedStateSnapshot>,
},
SnapshotSelfRatchets {
reply: oneshot::Sender<SelfRatchetsSnapshot>,
},
SnapshotSelfRatchet {
destination: DestinationHash,
reply: oneshot::Sender<Option<SelfRatchetSnapshot>>,
},
#[cfg(feature = "runtime-metrics")]
SnapshotMetrics {
reply: oneshot::Sender<RuntimeMetricsSnapshot>,
},
}
pub struct StreamInbound {
pub payload: std::vec::Vec<u8>,
pub eof: bool,
pub compressed: bool,
}
pub enum ResourceInbound {
Metadata(std::vec::Vec<u8>),
Chunk(std::vec::Vec<u8>),
Complete {
original_hash: ResourceHash,
total_size_bytes: u64,
},
Failed,
}
pub struct AddInterfaceCommand {
pub descriptor: InterfaceDescriptor,
pub logical_interface: InterfaceId,
pub inbound: TokioGrantConsumer,
pub egress: TokioGrantProducer,
pub connection: Option<ConnectionView>,
pub ifac: Option<crate::interfaces::IfacContext>,
}
#[derive(Debug)]
enum HostResourceStorage {
Owned(std::vec::Vec<u8>),
Shared(Arc<[u8]>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HostResourcePayloadError {
PrefixOutOfRange,
}
#[derive(Debug)]
pub struct HostResourcePayload {
storage: HostResourceStorage,
len: usize,
}
impl HostResourcePayload {
#[must_use]
pub fn as_slice(&self) -> &[u8] {
match &self.storage {
HostResourceStorage::Owned(bytes) => &bytes[..self.len],
HostResourceStorage::Shared(bytes) => &bytes[..self.len],
}
}
#[must_use]
pub fn len(&self) -> usize {
self.len
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn shared_prefix(bytes: Arc<[u8]>, len: usize) -> Result<Self, HostResourcePayloadError> {
if len > bytes.len() {
return Err(HostResourcePayloadError::PrefixOutOfRange);
}
Ok(Self {
storage: HostResourceStorage::Shared(bytes),
len,
})
}
}
impl AsRef<[u8]> for HostResourcePayload {
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl From<std::vec::Vec<u8>> for HostResourcePayload {
fn from(bytes: std::vec::Vec<u8>) -> Self {
let len = bytes.len();
Self {
storage: HostResourceStorage::Owned(bytes),
len,
}
}
}
impl From<Arc<[u8]>> for HostResourcePayload {
fn from(bytes: Arc<[u8]>) -> Self {
let len = bytes.len();
Self {
storage: HostResourceStorage::Shared(bytes),
len,
}
}
}
pub enum HostResourceMetadata {
None,
Packed(HostResourcePayload),
SentInFirstSegment {
packed_len: u32,
},
}
impl HostResourceMetadata {
pub(super) fn as_engine(&self) -> ResourceMetadata<'_> {
match self {
Self::None => ResourceMetadata::None,
Self::Packed(payload) => ResourceMetadata::Packed(payload.as_slice()),
Self::SentInFirstSegment { packed_len } => ResourceMetadata::SentInFirstSegment {
packed_len: *packed_len,
},
}
}
}
pub struct SendResourceHostCommand {
pub id: CommandId,
pub link_id: LinkId,
pub data: HostResourcePayload,
pub compressed_candidate: Option<HostResourcePayload>,
pub metadata: HostResourceMetadata,
pub request_id: Option<RequestId>,
}
pub struct SendResourceSegmentHostCommand {
pub id: CommandId,
pub link_id: LinkId,
pub data: HostResourcePayload,
pub compressed_candidate: Option<HostResourcePayload>,
pub metadata: HostResourceMetadata,
pub request_id: Option<RequestId>,
pub segment_index: u64,
pub total_segments: u64,
pub total_data_bytes: u64,
pub completion: oneshot::Sender<Settlement>,
}
pub struct RespondAnyHostCommand {
pub id: CommandId,
pub link_id: LinkId,
pub request_id: RequestId,
pub packed: HostResourcePayload,
pub compressed_candidate: Option<HostResourcePayload>,
pub completion: Option<oneshot::Sender<Settlement>>,
}
pub struct RequestAnyHostCommand {
pub id: CommandId,
pub link_id: LinkId,
pub path_hash: RequestPathHash,
pub data: HostResourcePayload,
pub response_timeout: crate::engine::RequestResponseTimeout,
pub maximum_response_bytes: crate::units::ByteLimit,
pub completion: oneshot::Sender<Result<(std::vec::Vec<u8>, RttMillis), SendRequestFailure>>,
}
pub struct ProvideDecompressedHostCommand {
pub link_id: LinkId,
pub hash: ResourceHash,
pub plaintext: HostResourcePayload,
}
#[cfg(test)]
mod tests;