use crate::generated_protocol::v1 as generated_protocol;
use serde::de::{self, SeqAccess, Visitor};
use serde::ser::SerializeTuple;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::error::Error;
use std::fmt;
pub use crate::wire::{OwnershipRequirement, ProtocolCodecError, RequestDirection};
pub const PROTOCOL_NAME: &str = crate::wire::PROTOCOL_NAME;
pub const PROTOCOL_VERSION: u16 = crate::wire::PROTOCOL_VERSION;
pub const DEFAULT_MAX_FRAME_BYTES: usize = crate::wire::DEFAULT_MAX_FRAME_BYTES;
pub const DEFAULT_COMPLETED_RESPONSE_CAP: usize = 10_000;
pub type RequestId = crate::wire::RequestId;
pub type ExtEnvelope = crate::wire::ExtEnvelope;
fn serialize_bare_newtype_tag<S, T>(serializer: S, tag: u64, payload: &T) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize,
{
let mut tuple = serializer.serialize_tuple(2)?;
tuple.serialize_element(&serde_bare::Uint(tag))?;
tuple.serialize_element(payload)?;
tuple.end()
}
pub fn to_generated_protocol_frame(
frame: &ProtocolFrame,
) -> Result<generated_protocol::ProtocolFrame, ProtocolCodecError> {
Ok(match frame {
ProtocolFrame::Request(frame) => {
generated_protocol::ProtocolFrame::RequestFrame(generated_protocol::RequestFrame {
schema: to_generated_protocol_schema(&frame.schema),
request_id: frame.request_id,
ownership: to_generated_ownership_scope(&frame.ownership),
payload: to_generated_request_payload(&frame.payload)?,
})
}
ProtocolFrame::Response(frame) => {
generated_protocol::ProtocolFrame::ResponseFrame(generated_protocol::ResponseFrame {
schema: to_generated_protocol_schema(&frame.schema),
request_id: frame.request_id,
ownership: to_generated_ownership_scope(&frame.ownership),
payload: to_generated_response_payload(&frame.payload)?,
})
}
ProtocolFrame::Event(frame) => {
generated_protocol::ProtocolFrame::EventFrame(generated_protocol::EventFrame {
schema: to_generated_protocol_schema(&frame.schema),
ownership: to_generated_ownership_scope(&frame.ownership),
payload: to_generated_event_payload(&frame.payload),
})
}
ProtocolFrame::SidecarRequest(frame) => {
generated_protocol::ProtocolFrame::SidecarRequestFrame(
generated_protocol::SidecarRequestFrame {
schema: to_generated_protocol_schema(&frame.schema),
request_id: frame.request_id,
ownership: to_generated_ownership_scope(&frame.ownership),
payload: to_generated_sidecar_request_payload(&frame.payload)?,
},
)
}
ProtocolFrame::SidecarResponse(frame) => {
generated_protocol::ProtocolFrame::SidecarResponseFrame(
generated_protocol::SidecarResponseFrame {
schema: to_generated_protocol_schema(&frame.schema),
request_id: frame.request_id,
ownership: to_generated_ownership_scope(&frame.ownership),
payload: to_generated_sidecar_response_payload(&frame.payload)?,
},
)
}
})
}
pub fn from_generated_protocol_frame(
frame: generated_protocol::ProtocolFrame,
) -> Result<ProtocolFrame, ProtocolCodecError> {
Ok(match frame {
generated_protocol::ProtocolFrame::RequestFrame(frame) => {
ProtocolFrame::Request(RequestFrame {
schema: from_generated_protocol_schema(frame.schema),
request_id: frame.request_id,
ownership: from_generated_ownership_scope(frame.ownership),
payload: from_generated_request_payload(frame.payload)?,
})
}
generated_protocol::ProtocolFrame::ResponseFrame(frame) => {
ProtocolFrame::Response(ResponseFrame {
schema: from_generated_protocol_schema(frame.schema),
request_id: frame.request_id,
ownership: from_generated_ownership_scope(frame.ownership),
payload: from_generated_response_payload(frame.payload)?,
})
}
generated_protocol::ProtocolFrame::EventFrame(frame) => ProtocolFrame::Event(EventFrame {
schema: from_generated_protocol_schema(frame.schema),
ownership: from_generated_ownership_scope(frame.ownership),
payload: from_generated_event_payload(frame.payload),
}),
generated_protocol::ProtocolFrame::SidecarRequestFrame(frame) => {
ProtocolFrame::SidecarRequest(SidecarRequestFrame {
schema: from_generated_protocol_schema(frame.schema),
request_id: frame.request_id,
ownership: from_generated_ownership_scope(frame.ownership),
payload: from_generated_sidecar_request_payload(frame.payload)?,
})
}
generated_protocol::ProtocolFrame::SidecarResponseFrame(frame) => {
ProtocolFrame::SidecarResponse(SidecarResponseFrame {
schema: from_generated_protocol_schema(frame.schema),
request_id: frame.request_id,
ownership: from_generated_ownership_scope(frame.ownership),
payload: from_generated_sidecar_response_payload(frame.payload)?,
})
}
})
}
fn to_generated_protocol_schema(schema: &ProtocolSchema) -> generated_protocol::ProtocolSchema {
schema.clone()
}
fn from_generated_protocol_schema(schema: generated_protocol::ProtocolSchema) -> ProtocolSchema {
schema
}
pub(crate) fn to_generated_ownership_scope(
ownership: &OwnershipScope,
) -> generated_protocol::OwnershipScope {
ownership.clone()
}
pub(crate) fn from_generated_ownership_scope(
ownership: generated_protocol::OwnershipScope,
) -> OwnershipScope {
ownership
}
fn to_generated_dispose_reason(reason: &DisposeReason) -> generated_protocol::DisposeReason {
match reason {
DisposeReason::Requested => generated_protocol::DisposeReason::Requested,
DisposeReason::ConnectionClosed => generated_protocol::DisposeReason::ConnectionClosed,
DisposeReason::HostShutdown => generated_protocol::DisposeReason::HostShutdown,
}
}
fn from_generated_dispose_reason(reason: generated_protocol::DisposeReason) -> DisposeReason {
match reason {
generated_protocol::DisposeReason::Requested => DisposeReason::Requested,
generated_protocol::DisposeReason::ConnectionClosed => DisposeReason::ConnectionClosed,
generated_protocol::DisposeReason::HostShutdown => DisposeReason::HostShutdown,
}
}
fn to_generated_filesystem_operation(
operation: &FilesystemOperation,
) -> generated_protocol::FilesystemOperation {
match operation {
FilesystemOperation::Read => generated_protocol::FilesystemOperation::Read,
FilesystemOperation::Write => generated_protocol::FilesystemOperation::Write,
FilesystemOperation::Stat => generated_protocol::FilesystemOperation::Stat,
FilesystemOperation::ReadDir => generated_protocol::FilesystemOperation::ReadDir,
FilesystemOperation::Mkdir => generated_protocol::FilesystemOperation::Mkdir,
FilesystemOperation::Remove => generated_protocol::FilesystemOperation::Remove,
FilesystemOperation::Rename => generated_protocol::FilesystemOperation::Rename,
}
}
fn from_generated_filesystem_operation(
operation: generated_protocol::FilesystemOperation,
) -> FilesystemOperation {
match operation {
generated_protocol::FilesystemOperation::Read => FilesystemOperation::Read,
generated_protocol::FilesystemOperation::Write => FilesystemOperation::Write,
generated_protocol::FilesystemOperation::Stat => FilesystemOperation::Stat,
generated_protocol::FilesystemOperation::ReadDir => FilesystemOperation::ReadDir,
generated_protocol::FilesystemOperation::Mkdir => FilesystemOperation::Mkdir,
generated_protocol::FilesystemOperation::Remove => FilesystemOperation::Remove,
generated_protocol::FilesystemOperation::Rename => FilesystemOperation::Rename,
}
}
fn to_generated_guest_filesystem_operation(
operation: &GuestFilesystemOperation,
) -> generated_protocol::GuestFilesystemOperation {
match operation {
GuestFilesystemOperation::ReadFile => {
generated_protocol::GuestFilesystemOperation::ReadFile
}
GuestFilesystemOperation::WriteFile => {
generated_protocol::GuestFilesystemOperation::WriteFile
}
GuestFilesystemOperation::CreateDir => {
generated_protocol::GuestFilesystemOperation::CreateDir
}
GuestFilesystemOperation::Mkdir => generated_protocol::GuestFilesystemOperation::Mkdir,
GuestFilesystemOperation::Exists => generated_protocol::GuestFilesystemOperation::Exists,
GuestFilesystemOperation::Stat => generated_protocol::GuestFilesystemOperation::Stat,
GuestFilesystemOperation::Lstat => generated_protocol::GuestFilesystemOperation::Lstat,
GuestFilesystemOperation::ReadDir => generated_protocol::GuestFilesystemOperation::ReadDir,
GuestFilesystemOperation::RemoveFile => {
generated_protocol::GuestFilesystemOperation::RemoveFile
}
GuestFilesystemOperation::RemoveDir => {
generated_protocol::GuestFilesystemOperation::RemoveDir
}
GuestFilesystemOperation::Rename => generated_protocol::GuestFilesystemOperation::Rename,
GuestFilesystemOperation::Realpath => {
generated_protocol::GuestFilesystemOperation::Realpath
}
GuestFilesystemOperation::Symlink => generated_protocol::GuestFilesystemOperation::Symlink,
GuestFilesystemOperation::ReadLink => {
generated_protocol::GuestFilesystemOperation::ReadLink
}
GuestFilesystemOperation::Link => generated_protocol::GuestFilesystemOperation::Link,
GuestFilesystemOperation::Chmod => generated_protocol::GuestFilesystemOperation::Chmod,
GuestFilesystemOperation::Chown => generated_protocol::GuestFilesystemOperation::Chown,
GuestFilesystemOperation::Utimes => generated_protocol::GuestFilesystemOperation::Utimes,
GuestFilesystemOperation::Truncate => {
generated_protocol::GuestFilesystemOperation::Truncate
}
GuestFilesystemOperation::Pread => generated_protocol::GuestFilesystemOperation::Pread,
}
}
fn from_generated_guest_filesystem_operation(
operation: generated_protocol::GuestFilesystemOperation,
) -> GuestFilesystemOperation {
match operation {
generated_protocol::GuestFilesystemOperation::ReadFile => {
GuestFilesystemOperation::ReadFile
}
generated_protocol::GuestFilesystemOperation::WriteFile => {
GuestFilesystemOperation::WriteFile
}
generated_protocol::GuestFilesystemOperation::CreateDir => {
GuestFilesystemOperation::CreateDir
}
generated_protocol::GuestFilesystemOperation::Mkdir => GuestFilesystemOperation::Mkdir,
generated_protocol::GuestFilesystemOperation::Exists => GuestFilesystemOperation::Exists,
generated_protocol::GuestFilesystemOperation::Stat => GuestFilesystemOperation::Stat,
generated_protocol::GuestFilesystemOperation::Lstat => GuestFilesystemOperation::Lstat,
generated_protocol::GuestFilesystemOperation::ReadDir => GuestFilesystemOperation::ReadDir,
generated_protocol::GuestFilesystemOperation::RemoveFile => {
GuestFilesystemOperation::RemoveFile
}
generated_protocol::GuestFilesystemOperation::RemoveDir => {
GuestFilesystemOperation::RemoveDir
}
generated_protocol::GuestFilesystemOperation::Rename => GuestFilesystemOperation::Rename,
generated_protocol::GuestFilesystemOperation::Realpath => {
GuestFilesystemOperation::Realpath
}
generated_protocol::GuestFilesystemOperation::Symlink => GuestFilesystemOperation::Symlink,
generated_protocol::GuestFilesystemOperation::ReadLink => {
GuestFilesystemOperation::ReadLink
}
generated_protocol::GuestFilesystemOperation::Link => GuestFilesystemOperation::Link,
generated_protocol::GuestFilesystemOperation::Chmod => GuestFilesystemOperation::Chmod,
generated_protocol::GuestFilesystemOperation::Chown => GuestFilesystemOperation::Chown,
generated_protocol::GuestFilesystemOperation::Utimes => GuestFilesystemOperation::Utimes,
generated_protocol::GuestFilesystemOperation::Truncate => {
GuestFilesystemOperation::Truncate
}
generated_protocol::GuestFilesystemOperation::Pread => GuestFilesystemOperation::Pread,
}
}
fn to_generated_permission_mode(mode: &PermissionMode) -> generated_protocol::PermissionMode {
mode.clone()
}
fn from_generated_permission_mode(mode: generated_protocol::PermissionMode) -> PermissionMode {
mode
}
fn to_generated_root_filesystem_entry_encoding(
encoding: &RootFilesystemEntryEncoding,
) -> generated_protocol::RootFilesystemEntryEncoding {
match encoding {
RootFilesystemEntryEncoding::Utf8 => generated_protocol::RootFilesystemEntryEncoding::Utf8,
RootFilesystemEntryEncoding::Base64 => {
generated_protocol::RootFilesystemEntryEncoding::Base64
}
}
}
fn from_generated_root_filesystem_entry_encoding(
encoding: generated_protocol::RootFilesystemEntryEncoding,
) -> RootFilesystemEntryEncoding {
match encoding {
generated_protocol::RootFilesystemEntryEncoding::Utf8 => RootFilesystemEntryEncoding::Utf8,
generated_protocol::RootFilesystemEntryEncoding::Base64 => {
RootFilesystemEntryEncoding::Base64
}
}
}
fn to_generated_stream_channel(channel: &StreamChannel) -> generated_protocol::StreamChannel {
match channel {
StreamChannel::Stdout => generated_protocol::StreamChannel::Stdout,
StreamChannel::Stderr => generated_protocol::StreamChannel::Stderr,
}
}
fn from_generated_stream_channel(channel: generated_protocol::StreamChannel) -> StreamChannel {
match channel {
generated_protocol::StreamChannel::Stdout => StreamChannel::Stdout,
generated_protocol::StreamChannel::Stderr => StreamChannel::Stderr,
}
}
fn to_generated_vm_lifecycle_state(
state: &VmLifecycleState,
) -> generated_protocol::VmLifecycleState {
match state {
VmLifecycleState::Creating => generated_protocol::VmLifecycleState::Creating,
VmLifecycleState::Ready => generated_protocol::VmLifecycleState::Ready,
VmLifecycleState::Disposing => generated_protocol::VmLifecycleState::Disposing,
VmLifecycleState::Disposed => generated_protocol::VmLifecycleState::Disposed,
VmLifecycleState::Failed => generated_protocol::VmLifecycleState::Failed,
}
}
fn from_generated_vm_lifecycle_state(
state: generated_protocol::VmLifecycleState,
) -> VmLifecycleState {
match state {
generated_protocol::VmLifecycleState::Creating => VmLifecycleState::Creating,
generated_protocol::VmLifecycleState::Ready => VmLifecycleState::Ready,
generated_protocol::VmLifecycleState::Disposing => VmLifecycleState::Disposing,
generated_protocol::VmLifecycleState::Disposed => VmLifecycleState::Disposed,
generated_protocol::VmLifecycleState::Failed => VmLifecycleState::Failed,
}
}
fn to_generated_guest_filesystem_stat(
stat: &GuestFilesystemStat,
) -> generated_protocol::GuestFilesystemStat {
stat.clone()
}
fn from_generated_guest_filesystem_stat(
stat: generated_protocol::GuestFilesystemStat,
) -> GuestFilesystemStat {
stat
}
fn to_generated_process_snapshot_entry(
entry: &ProcessSnapshotEntry,
) -> generated_protocol::ProcessSnapshotEntry {
entry.clone()
}
fn from_generated_process_snapshot_entry(
entry: generated_protocol::ProcessSnapshotEntry,
) -> ProcessSnapshotEntry {
entry
}
fn to_generated_ext_envelope(envelope: &ExtEnvelope) -> generated_protocol::ExtEnvelope {
envelope.clone()
}
fn from_generated_ext_envelope(envelope: generated_protocol::ExtEnvelope) -> ExtEnvelope {
envelope
}
fn to_generated_request_payload(
payload: &RequestPayload,
) -> Result<generated_protocol::RequestPayload, ProtocolCodecError> {
Ok(match payload {
RequestPayload::Authenticate(inner) => {
generated_protocol::RequestPayload::AuthenticateRequest(inner.clone())
}
RequestPayload::OpenSession(inner) => {
generated_protocol::RequestPayload::OpenSessionRequest(inner.clone())
}
RequestPayload::CreateVm(inner) => {
generated_protocol::RequestPayload::CreateVmRequest(inner.clone())
}
RequestPayload::DisposeVm(inner) => generated_protocol::RequestPayload::DisposeVmRequest(
generated_protocol::DisposeVmRequest {
reason: to_generated_dispose_reason(&inner.reason),
},
),
RequestPayload::BootstrapRootFilesystem(inner) => {
generated_protocol::RequestPayload::BootstrapRootFilesystemRequest(inner.clone())
}
RequestPayload::ConfigureVm(inner) => {
generated_protocol::RequestPayload::ConfigureVmRequest(inner.clone())
}
RequestPayload::RegisterHostCallbacks(inner) => {
generated_protocol::RequestPayload::RegisterHostCallbacksRequest(inner.clone())
}
RequestPayload::CreateLayer(_) => generated_protocol::RequestPayload::CreateLayerRequest,
RequestPayload::SealLayer(inner) => {
generated_protocol::RequestPayload::SealLayerRequest(inner.clone())
}
RequestPayload::ImportSnapshot(inner) => {
generated_protocol::RequestPayload::ImportSnapshotRequest(inner.clone())
}
RequestPayload::ExportSnapshot(inner) => {
generated_protocol::RequestPayload::ExportSnapshotRequest(inner.clone())
}
RequestPayload::CreateOverlay(inner) => {
generated_protocol::RequestPayload::CreateOverlayRequest(inner.clone())
}
RequestPayload::GuestFilesystemCall(inner) => {
generated_protocol::RequestPayload::GuestFilesystemCallRequest(inner.clone())
}
RequestPayload::SnapshotRootFilesystem(_) => {
generated_protocol::RequestPayload::SnapshotRootFilesystemRequest
}
RequestPayload::Execute(inner) => {
generated_protocol::RequestPayload::ExecuteRequest(inner.clone())
}
RequestPayload::WriteStdin(inner) => {
generated_protocol::RequestPayload::WriteStdinRequest(inner.clone())
}
RequestPayload::CloseStdin(inner) => {
generated_protocol::RequestPayload::CloseStdinRequest(inner.clone())
}
RequestPayload::KillProcess(inner) => {
generated_protocol::RequestPayload::KillProcessRequest(inner.clone())
}
RequestPayload::GetProcessSnapshot(_) => {
generated_protocol::RequestPayload::GetProcessSnapshotRequest
}
RequestPayload::FindListener(inner) => {
generated_protocol::RequestPayload::FindListenerRequest(inner.clone())
}
RequestPayload::FindBoundUdp(inner) => {
generated_protocol::RequestPayload::FindBoundUdpRequest(inner.clone())
}
RequestPayload::VmFetch(inner) => {
generated_protocol::RequestPayload::VmFetchRequest(inner.clone())
}
RequestPayload::GetSignalState(inner) => {
generated_protocol::RequestPayload::GetSignalStateRequest(inner.clone())
}
RequestPayload::GetZombieTimerCount(_) => {
generated_protocol::RequestPayload::GetZombieTimerCountRequest
}
RequestPayload::HostFilesystemCall(inner) => {
generated_protocol::RequestPayload::HostFilesystemCallRequest(
generated_protocol::HostFilesystemCallRequest {
operation: to_generated_filesystem_operation(&inner.operation),
path: inner.path.clone(),
payload_size_bytes: inner.payload_size_bytes,
},
)
}
RequestPayload::PersistenceLoad(inner) => {
generated_protocol::RequestPayload::PersistenceLoadRequest(inner.clone())
}
RequestPayload::PersistenceFlush(inner) => {
generated_protocol::RequestPayload::PersistenceFlushRequest(inner.clone())
}
RequestPayload::Ext(inner) => {
generated_protocol::RequestPayload::ExtEnvelope(to_generated_ext_envelope(inner))
}
})
}
fn from_generated_request_payload(
payload: generated_protocol::RequestPayload,
) -> Result<RequestPayload, ProtocolCodecError> {
Ok(match payload {
generated_protocol::RequestPayload::AuthenticateRequest(inner) => {
RequestPayload::Authenticate(inner)
}
generated_protocol::RequestPayload::OpenSessionRequest(inner) => {
RequestPayload::OpenSession(inner)
}
generated_protocol::RequestPayload::CreateVmRequest(inner) => {
RequestPayload::CreateVm(inner)
}
generated_protocol::RequestPayload::DisposeVmRequest(inner) => {
RequestPayload::DisposeVm(DisposeVmRequest {
reason: from_generated_dispose_reason(inner.reason),
})
}
generated_protocol::RequestPayload::BootstrapRootFilesystemRequest(inner) => {
RequestPayload::BootstrapRootFilesystem(inner)
}
generated_protocol::RequestPayload::ConfigureVmRequest(inner) => {
RequestPayload::ConfigureVm(inner)
}
generated_protocol::RequestPayload::RegisterHostCallbacksRequest(inner) => {
RequestPayload::RegisterHostCallbacks(inner)
}
generated_protocol::RequestPayload::CreateLayerRequest => {
RequestPayload::CreateLayer(CreateLayerRequest {})
}
generated_protocol::RequestPayload::SealLayerRequest(inner) => {
RequestPayload::SealLayer(inner)
}
generated_protocol::RequestPayload::ImportSnapshotRequest(inner) => {
RequestPayload::ImportSnapshot(inner)
}
generated_protocol::RequestPayload::ExportSnapshotRequest(inner) => {
RequestPayload::ExportSnapshot(inner)
}
generated_protocol::RequestPayload::CreateOverlayRequest(inner) => {
RequestPayload::CreateOverlay(inner)
}
generated_protocol::RequestPayload::GuestFilesystemCallRequest(inner) => {
RequestPayload::GuestFilesystemCall(inner)
}
generated_protocol::RequestPayload::SnapshotRootFilesystemRequest => {
RequestPayload::SnapshotRootFilesystem(SnapshotRootFilesystemRequest {})
}
generated_protocol::RequestPayload::ExecuteRequest(inner) => RequestPayload::Execute(inner),
generated_protocol::RequestPayload::WriteStdinRequest(inner) => {
RequestPayload::WriteStdin(inner)
}
generated_protocol::RequestPayload::CloseStdinRequest(inner) => {
RequestPayload::CloseStdin(inner)
}
generated_protocol::RequestPayload::KillProcessRequest(inner) => {
RequestPayload::KillProcess(inner)
}
generated_protocol::RequestPayload::GetProcessSnapshotRequest => {
RequestPayload::GetProcessSnapshot(GetProcessSnapshotRequest {})
}
generated_protocol::RequestPayload::FindListenerRequest(inner) => {
RequestPayload::FindListener(inner)
}
generated_protocol::RequestPayload::FindBoundUdpRequest(inner) => {
RequestPayload::FindBoundUdp(inner)
}
generated_protocol::RequestPayload::VmFetchRequest(inner) => RequestPayload::VmFetch(inner),
generated_protocol::RequestPayload::GetSignalStateRequest(inner) => {
RequestPayload::GetSignalState(inner)
}
generated_protocol::RequestPayload::GetZombieTimerCountRequest => {
RequestPayload::GetZombieTimerCount(GetZombieTimerCountRequest {})
}
generated_protocol::RequestPayload::HostFilesystemCallRequest(inner) => {
RequestPayload::HostFilesystemCall(HostFilesystemCallRequest {
operation: from_generated_filesystem_operation(inner.operation),
path: inner.path,
payload_size_bytes: inner.payload_size_bytes,
})
}
generated_protocol::RequestPayload::PersistenceLoadRequest(inner) => {
RequestPayload::PersistenceLoad(inner)
}
generated_protocol::RequestPayload::PersistenceFlushRequest(inner) => {
RequestPayload::PersistenceFlush(inner)
}
generated_protocol::RequestPayload::ExtEnvelope(inner) => {
RequestPayload::Ext(from_generated_ext_envelope(inner))
}
})
}
fn to_generated_response_payload(
payload: &ResponsePayload,
) -> Result<generated_protocol::ResponsePayload, ProtocolCodecError> {
Ok(match payload {
ResponsePayload::Authenticated(inner) => {
generated_protocol::ResponsePayload::AuthenticatedResponse(inner.clone())
}
ResponsePayload::SessionOpened(inner) => {
generated_protocol::ResponsePayload::SessionOpenedResponse(inner.clone())
}
ResponsePayload::VmCreated(inner) => {
generated_protocol::ResponsePayload::VmCreatedResponse(inner.clone())
}
ResponsePayload::VmDisposed(inner) => {
generated_protocol::ResponsePayload::VmDisposedResponse(inner.clone())
}
ResponsePayload::RootFilesystemBootstrapped(inner) => {
generated_protocol::ResponsePayload::RootFilesystemBootstrappedResponse(inner.clone())
}
ResponsePayload::VmConfigured(inner) => {
generated_protocol::ResponsePayload::VmConfiguredResponse(inner.clone())
}
ResponsePayload::HostCallbacksRegistered(inner) => {
generated_protocol::ResponsePayload::HostCallbacksRegisteredResponse(inner.clone())
}
ResponsePayload::LayerCreated(inner) => {
generated_protocol::ResponsePayload::LayerCreatedResponse(inner.clone())
}
ResponsePayload::LayerSealed(inner) => {
generated_protocol::ResponsePayload::LayerSealedResponse(inner.clone())
}
ResponsePayload::SnapshotImported(inner) => {
generated_protocol::ResponsePayload::SnapshotImportedResponse(inner.clone())
}
ResponsePayload::SnapshotExported(inner) => {
generated_protocol::ResponsePayload::SnapshotExportedResponse(
generated_protocol::SnapshotExportedResponse {
layer_id: inner.layer_id.clone(),
entries: inner.entries.clone(),
},
)
}
ResponsePayload::OverlayCreated(inner) => {
generated_protocol::ResponsePayload::OverlayCreatedResponse(inner.clone())
}
ResponsePayload::GuestFilesystemResult(inner) => {
generated_protocol::ResponsePayload::GuestFilesystemResultResponse(
generated_protocol::GuestFilesystemResultResponse {
operation: to_generated_guest_filesystem_operation(&inner.operation),
path: inner.path.clone(),
content: inner.content.clone(),
encoding: inner
.encoding
.as_ref()
.map(to_generated_root_filesystem_entry_encoding),
entries: inner.entries.clone(),
stat: inner.stat.as_ref().map(to_generated_guest_filesystem_stat),
exists: inner.exists,
target: inner.target.clone(),
},
)
}
ResponsePayload::RootFilesystemSnapshot(inner) => {
generated_protocol::ResponsePayload::RootFilesystemSnapshotResponse(
generated_protocol::RootFilesystemSnapshotResponse {
entries: inner.entries.clone(),
},
)
}
ResponsePayload::ProcessStarted(inner) => {
generated_protocol::ResponsePayload::ProcessStartedResponse(inner.clone())
}
ResponsePayload::StdinWritten(inner) => {
generated_protocol::ResponsePayload::StdinWrittenResponse(inner.clone())
}
ResponsePayload::StdinClosed(inner) => {
generated_protocol::ResponsePayload::StdinClosedResponse(inner.clone())
}
ResponsePayload::ProcessKilled(inner) => {
generated_protocol::ResponsePayload::ProcessKilledResponse(inner.clone())
}
ResponsePayload::ProcessSnapshot(inner) => {
generated_protocol::ResponsePayload::ProcessSnapshotResponse(
generated_protocol::ProcessSnapshotResponse {
processes: inner
.processes
.iter()
.map(to_generated_process_snapshot_entry)
.collect(),
},
)
}
ResponsePayload::ListenerSnapshot(inner) => {
generated_protocol::ResponsePayload::ListenerSnapshotResponse(inner.clone())
}
ResponsePayload::BoundUdpSnapshot(inner) => {
generated_protocol::ResponsePayload::BoundUdpSnapshotResponse(inner.clone())
}
ResponsePayload::VmFetchResult(inner) => {
generated_protocol::ResponsePayload::VmFetchResponse(inner.clone())
}
ResponsePayload::SignalState(inner) => {
generated_protocol::ResponsePayload::SignalStateResponse(inner.clone())
}
ResponsePayload::ZombieTimerCount(inner) => {
generated_protocol::ResponsePayload::ZombieTimerCountResponse(inner.clone())
}
ResponsePayload::FilesystemResult(inner) => {
generated_protocol::ResponsePayload::FilesystemResultResponse(
generated_protocol::FilesystemResultResponse {
operation: to_generated_filesystem_operation(&inner.operation),
status: inner.status.clone(),
payload_size_bytes: inner.payload_size_bytes,
},
)
}
ResponsePayload::PermissionDecision(inner) => {
generated_protocol::ResponsePayload::PermissionDecisionResponse(
generated_protocol::PermissionDecisionResponse {
capability: inner.capability.clone(),
decision: to_generated_permission_mode(&inner.decision),
},
)
}
ResponsePayload::PersistenceState(inner) => {
generated_protocol::ResponsePayload::PersistenceStateResponse(inner.clone())
}
ResponsePayload::PersistenceFlushed(inner) => {
generated_protocol::ResponsePayload::PersistenceFlushedResponse(inner.clone())
}
ResponsePayload::Rejected(inner) => {
generated_protocol::ResponsePayload::RejectedResponse(inner.clone())
}
ResponsePayload::ExtResult(inner) => {
generated_protocol::ResponsePayload::ExtEnvelope(to_generated_ext_envelope(inner))
}
})
}
fn from_generated_response_payload(
payload: generated_protocol::ResponsePayload,
) -> Result<ResponsePayload, ProtocolCodecError> {
Ok(match payload {
generated_protocol::ResponsePayload::AuthenticatedResponse(inner) => {
ResponsePayload::Authenticated(inner)
}
generated_protocol::ResponsePayload::SessionOpenedResponse(inner) => {
ResponsePayload::SessionOpened(inner)
}
generated_protocol::ResponsePayload::VmCreatedResponse(inner) => {
ResponsePayload::VmCreated(inner)
}
generated_protocol::ResponsePayload::VmDisposedResponse(inner) => {
ResponsePayload::VmDisposed(inner)
}
generated_protocol::ResponsePayload::RootFilesystemBootstrappedResponse(inner) => {
ResponsePayload::RootFilesystemBootstrapped(inner)
}
generated_protocol::ResponsePayload::VmConfiguredResponse(inner) => {
ResponsePayload::VmConfigured(inner)
}
generated_protocol::ResponsePayload::HostCallbacksRegisteredResponse(inner) => {
ResponsePayload::HostCallbacksRegistered(inner)
}
generated_protocol::ResponsePayload::LayerCreatedResponse(inner) => {
ResponsePayload::LayerCreated(inner)
}
generated_protocol::ResponsePayload::LayerSealedResponse(inner) => {
ResponsePayload::LayerSealed(inner)
}
generated_protocol::ResponsePayload::SnapshotImportedResponse(inner) => {
ResponsePayload::SnapshotImported(inner)
}
generated_protocol::ResponsePayload::SnapshotExportedResponse(inner) => {
ResponsePayload::SnapshotExported(SnapshotExportedResponse {
layer_id: inner.layer_id,
entries: inner.entries,
})
}
generated_protocol::ResponsePayload::OverlayCreatedResponse(inner) => {
ResponsePayload::OverlayCreated(inner)
}
generated_protocol::ResponsePayload::GuestFilesystemResultResponse(inner) => {
ResponsePayload::GuestFilesystemResult(GuestFilesystemResultResponse {
operation: from_generated_guest_filesystem_operation(inner.operation),
path: inner.path,
content: inner.content,
encoding: inner
.encoding
.map(from_generated_root_filesystem_entry_encoding),
entries: inner.entries,
stat: inner.stat.map(from_generated_guest_filesystem_stat),
exists: inner.exists,
target: inner.target,
})
}
generated_protocol::ResponsePayload::RootFilesystemSnapshotResponse(inner) => {
ResponsePayload::RootFilesystemSnapshot(RootFilesystemSnapshotResponse {
entries: inner.entries,
})
}
generated_protocol::ResponsePayload::ProcessStartedResponse(inner) => {
ResponsePayload::ProcessStarted(inner)
}
generated_protocol::ResponsePayload::StdinWrittenResponse(inner) => {
ResponsePayload::StdinWritten(inner)
}
generated_protocol::ResponsePayload::StdinClosedResponse(inner) => {
ResponsePayload::StdinClosed(inner)
}
generated_protocol::ResponsePayload::ProcessKilledResponse(inner) => {
ResponsePayload::ProcessKilled(inner)
}
generated_protocol::ResponsePayload::ProcessSnapshotResponse(inner) => {
ResponsePayload::ProcessSnapshot(ProcessSnapshotResponse {
processes: inner
.processes
.into_iter()
.map(from_generated_process_snapshot_entry)
.collect(),
})
}
generated_protocol::ResponsePayload::ListenerSnapshotResponse(inner) => {
ResponsePayload::ListenerSnapshot(inner)
}
generated_protocol::ResponsePayload::BoundUdpSnapshotResponse(inner) => {
ResponsePayload::BoundUdpSnapshot(inner)
}
generated_protocol::ResponsePayload::VmFetchResponse(inner) => {
ResponsePayload::VmFetchResult(inner)
}
generated_protocol::ResponsePayload::SignalStateResponse(inner) => {
ResponsePayload::SignalState(inner)
}
generated_protocol::ResponsePayload::ZombieTimerCountResponse(inner) => {
ResponsePayload::ZombieTimerCount(inner)
}
generated_protocol::ResponsePayload::FilesystemResultResponse(inner) => {
ResponsePayload::FilesystemResult(FilesystemResultResponse {
operation: from_generated_filesystem_operation(inner.operation),
status: inner.status,
payload_size_bytes: inner.payload_size_bytes,
})
}
generated_protocol::ResponsePayload::PermissionDecisionResponse(inner) => {
ResponsePayload::PermissionDecision(PermissionDecisionResponse {
capability: inner.capability,
decision: from_generated_permission_mode(inner.decision),
})
}
generated_protocol::ResponsePayload::PersistenceStateResponse(inner) => {
ResponsePayload::PersistenceState(inner)
}
generated_protocol::ResponsePayload::PersistenceFlushedResponse(inner) => {
ResponsePayload::PersistenceFlushed(inner)
}
generated_protocol::ResponsePayload::RejectedResponse(inner) => {
ResponsePayload::Rejected(inner)
}
generated_protocol::ResponsePayload::ExtEnvelope(inner) => {
ResponsePayload::ExtResult(from_generated_ext_envelope(inner))
}
})
}
fn to_generated_event_payload(payload: &EventPayload) -> generated_protocol::EventPayload {
match payload {
EventPayload::VmLifecycle(inner) => generated_protocol::EventPayload::VmLifecycleEvent(
generated_protocol::VmLifecycleEvent {
state: to_generated_vm_lifecycle_state(&inner.state),
},
),
EventPayload::ProcessOutput(inner) => generated_protocol::EventPayload::ProcessOutputEvent(
generated_protocol::ProcessOutputEvent {
process_id: inner.process_id.clone(),
channel: to_generated_stream_channel(&inner.channel),
chunk: inner.chunk.clone(),
},
),
EventPayload::ProcessExited(inner) => {
generated_protocol::EventPayload::ProcessExitedEvent(inner.clone())
}
EventPayload::Structured(inner) => {
generated_protocol::EventPayload::StructuredEvent(inner.clone())
}
EventPayload::Ext(inner) => {
generated_protocol::EventPayload::ExtEnvelope(to_generated_ext_envelope(inner))
}
}
}
fn from_generated_event_payload(payload: generated_protocol::EventPayload) -> EventPayload {
match payload {
generated_protocol::EventPayload::VmLifecycleEvent(inner) => {
EventPayload::VmLifecycle(VmLifecycleEvent {
state: from_generated_vm_lifecycle_state(inner.state),
})
}
generated_protocol::EventPayload::ProcessOutputEvent(inner) => {
EventPayload::ProcessOutput(ProcessOutputEvent {
process_id: inner.process_id,
channel: from_generated_stream_channel(inner.channel),
chunk: inner.chunk,
})
}
generated_protocol::EventPayload::ProcessExitedEvent(inner) => {
EventPayload::ProcessExited(inner)
}
generated_protocol::EventPayload::StructuredEvent(inner) => EventPayload::Structured(inner),
generated_protocol::EventPayload::ExtEnvelope(inner) => {
EventPayload::Ext(from_generated_ext_envelope(inner))
}
}
}
fn to_generated_sidecar_request_payload(
payload: &SidecarRequestPayload,
) -> Result<generated_protocol::SidecarRequestPayload, ProtocolCodecError> {
Ok(match payload {
SidecarRequestPayload::HostCallback(inner) => {
generated_protocol::SidecarRequestPayload::HostCallbackRequest(inner.clone())
}
SidecarRequestPayload::JsBridgeCall(inner) => {
generated_protocol::SidecarRequestPayload::JsBridgeCallRequest(inner.clone())
}
SidecarRequestPayload::Ext(inner) => {
generated_protocol::SidecarRequestPayload::ExtEnvelope(to_generated_ext_envelope(inner))
}
})
}
fn from_generated_sidecar_request_payload(
payload: generated_protocol::SidecarRequestPayload,
) -> Result<SidecarRequestPayload, ProtocolCodecError> {
Ok(match payload {
generated_protocol::SidecarRequestPayload::HostCallbackRequest(inner) => {
SidecarRequestPayload::HostCallback(inner)
}
generated_protocol::SidecarRequestPayload::JsBridgeCallRequest(inner) => {
SidecarRequestPayload::JsBridgeCall(inner)
}
generated_protocol::SidecarRequestPayload::ExtEnvelope(inner) => {
SidecarRequestPayload::Ext(from_generated_ext_envelope(inner))
}
})
}
fn to_generated_sidecar_response_payload(
payload: &SidecarResponsePayload,
) -> Result<generated_protocol::SidecarResponsePayload, ProtocolCodecError> {
Ok(match payload {
SidecarResponsePayload::HostCallbackResult(inner) => {
generated_protocol::SidecarResponsePayload::HostCallbackResultResponse(inner.clone())
}
SidecarResponsePayload::JsBridgeResult(inner) => {
generated_protocol::SidecarResponsePayload::JsBridgeResultResponse(inner.clone())
}
SidecarResponsePayload::ExtResult(inner) => {
generated_protocol::SidecarResponsePayload::ExtEnvelope(to_generated_ext_envelope(
inner,
))
}
})
}
fn from_generated_sidecar_response_payload(
payload: generated_protocol::SidecarResponsePayload,
) -> Result<SidecarResponsePayload, ProtocolCodecError> {
Ok(match payload {
generated_protocol::SidecarResponsePayload::HostCallbackResultResponse(inner) => {
SidecarResponsePayload::HostCallbackResult(inner)
}
generated_protocol::SidecarResponsePayload::JsBridgeResultResponse(inner) => {
SidecarResponsePayload::JsBridgeResult(inner)
}
generated_protocol::SidecarResponsePayload::ExtEnvelope(inner) => {
SidecarResponsePayload::ExtResult(from_generated_ext_envelope(inner))
}
})
}
macro_rules! impl_bare_newtype_union_enum {
(
$name:ident,
$json_name:ident,
$(#[$json_attr:meta])*
{
$($variant:ident($ty:ty) = $tag:literal),+ $(,)?
}
) => {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
$(#[$json_attr])*
enum $json_name {
$($variant($ty)),+
}
impl From<&$name> for $json_name {
fn from(value: &$name) -> Self {
match value {
$($name::$variant(inner) => Self::$variant(inner.clone()),)+
}
}
}
impl From<$json_name> for $name {
fn from(value: $json_name) -> Self {
match value {
$($json_name::$variant(inner) => Self::$variant(inner),)+
}
}
}
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
$json_name::from(self).serialize(serializer)
} else {
match self {
$(Self::$variant(inner) => serialize_bare_newtype_tag(serializer, $tag, inner),)+
}
}
}
}
impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
Ok($json_name::deserialize(deserializer)?.into())
} else {
struct UnionVisitor;
impl<'de> Visitor<'de> for UnionVisitor {
type Value = $name;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "a {} BARE union", stringify!($name))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let serde_bare::Uint(tag) = seq
.next_element()?
.ok_or_else(|| de::Error::custom(concat!("missing ", stringify!($name), " tag")))?;
match tag {
$(
$tag => {
let payload = seq.next_element::<$ty>()?.ok_or_else(|| {
de::Error::custom(format!(
"missing {} payload for tag {}",
stringify!($variant),
$tag
))
})?;
Ok($name::$variant(payload))
}
)+
_ => Err(de::Error::custom(format!(
"unknown {} tag: {}",
stringify!($name),
tag
))),
}
}
}
deserializer.deserialize_tuple(2, UnionVisitor)
}
}
}
};
}
pub type ProtocolSchema = crate::wire::ProtocolSchema;
pub type OwnershipScope = crate::wire::OwnershipScope;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProtocolFrame {
Request(RequestFrame),
Response(ResponseFrame),
Event(EventFrame),
SidecarRequest(SidecarRequestFrame),
SidecarResponse(SidecarResponseFrame),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RequestFrame {
pub schema: ProtocolSchema,
pub request_id: RequestId,
pub ownership: OwnershipScope,
pub payload: RequestPayload,
}
impl RequestFrame {
pub fn new(request_id: RequestId, ownership: OwnershipScope, payload: RequestPayload) -> Self {
Self {
schema: ProtocolSchema::current(),
request_id,
ownership,
payload,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResponseFrame {
pub schema: ProtocolSchema,
pub request_id: RequestId,
pub ownership: OwnershipScope,
pub payload: ResponsePayload,
}
impl ResponseFrame {
pub fn new(request_id: RequestId, ownership: OwnershipScope, payload: ResponsePayload) -> Self {
Self {
schema: ProtocolSchema::current(),
request_id,
ownership,
payload,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SidecarRequestFrame {
pub schema: ProtocolSchema,
pub request_id: RequestId,
pub ownership: OwnershipScope,
pub payload: SidecarRequestPayload,
}
impl SidecarRequestFrame {
pub fn new(
request_id: RequestId,
ownership: OwnershipScope,
payload: SidecarRequestPayload,
) -> Self {
Self {
schema: ProtocolSchema::current(),
request_id,
ownership,
payload,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SidecarResponseFrame {
pub schema: ProtocolSchema,
pub request_id: RequestId,
pub ownership: OwnershipScope,
pub payload: SidecarResponsePayload,
}
impl SidecarResponseFrame {
pub fn new(
request_id: RequestId,
ownership: OwnershipScope,
payload: SidecarResponsePayload,
) -> Self {
Self {
schema: ProtocolSchema::current(),
request_id,
ownership,
payload,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct EventFrame {
pub schema: ProtocolSchema,
pub ownership: OwnershipScope,
pub payload: EventPayload,
}
impl EventFrame {
pub fn new(ownership: OwnershipScope, payload: EventPayload) -> Self {
Self {
schema: ProtocolSchema::current(),
ownership,
payload,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RequestPayload {
Authenticate(AuthenticateRequest),
OpenSession(OpenSessionRequest),
CreateVm(CreateVmRequest),
DisposeVm(DisposeVmRequest),
BootstrapRootFilesystem(BootstrapRootFilesystemRequest),
ConfigureVm(ConfigureVmRequest),
RegisterHostCallbacks(RegisterHostCallbacksRequest),
CreateLayer(CreateLayerRequest),
SealLayer(SealLayerRequest),
ImportSnapshot(ImportSnapshotRequest),
ExportSnapshot(ExportSnapshotRequest),
CreateOverlay(CreateOverlayRequest),
GuestFilesystemCall(GuestFilesystemCallRequest),
SnapshotRootFilesystem(SnapshotRootFilesystemRequest),
Execute(ExecuteRequest),
WriteStdin(WriteStdinRequest),
CloseStdin(CloseStdinRequest),
KillProcess(KillProcessRequest),
GetProcessSnapshot(GetProcessSnapshotRequest),
FindListener(FindListenerRequest),
FindBoundUdp(FindBoundUdpRequest),
VmFetch(VmFetchRequest),
GetSignalState(GetSignalStateRequest),
GetZombieTimerCount(GetZombieTimerCountRequest),
HostFilesystemCall(HostFilesystemCallRequest),
PersistenceLoad(PersistenceLoadRequest),
PersistenceFlush(PersistenceFlushRequest),
Ext(ExtEnvelope),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResponsePayload {
Authenticated(AuthenticatedResponse),
SessionOpened(SessionOpenedResponse),
VmCreated(VmCreatedResponse),
VmDisposed(VmDisposedResponse),
RootFilesystemBootstrapped(RootFilesystemBootstrappedResponse),
VmConfigured(VmConfiguredResponse),
HostCallbacksRegistered(HostCallbacksRegisteredResponse),
LayerCreated(LayerCreatedResponse),
LayerSealed(LayerSealedResponse),
SnapshotImported(SnapshotImportedResponse),
SnapshotExported(SnapshotExportedResponse),
OverlayCreated(OverlayCreatedResponse),
GuestFilesystemResult(GuestFilesystemResultResponse),
RootFilesystemSnapshot(RootFilesystemSnapshotResponse),
ProcessStarted(ProcessStartedResponse),
StdinWritten(StdinWrittenResponse),
StdinClosed(StdinClosedResponse),
ProcessKilled(ProcessKilledResponse),
ProcessSnapshot(ProcessSnapshotResponse),
ListenerSnapshot(ListenerSnapshotResponse),
BoundUdpSnapshot(BoundUdpSnapshotResponse),
VmFetchResult(VmFetchResponse),
SignalState(SignalStateResponse),
ZombieTimerCount(ZombieTimerCountResponse),
FilesystemResult(FilesystemResultResponse),
PermissionDecision(PermissionDecisionResponse),
PersistenceState(PersistenceStateResponse),
PersistenceFlushed(PersistenceFlushedResponse),
Rejected(RejectedResponse),
ExtResult(ExtEnvelope),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SidecarRequestPayload {
HostCallback(HostCallbackRequest),
JsBridgeCall(JsBridgeCallRequest),
Ext(ExtEnvelope),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SidecarResponsePayload {
HostCallbackResult(HostCallbackResultResponse),
JsBridgeResult(JsBridgeResultResponse),
ExtResult(ExtEnvelope),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EventPayload {
VmLifecycle(VmLifecycleEvent),
ProcessOutput(ProcessOutputEvent),
ProcessExited(ProcessExitedEvent),
Structured(StructuredEvent),
Ext(ExtEnvelope),
}
pub type SidecarPlacement = crate::wire::SidecarPlacement;
pub type SidecarPlacementShared = crate::wire::SidecarPlacementShared;
pub type SidecarPlacementExplicit = crate::wire::SidecarPlacementExplicit;
pub type GuestRuntimeKind = crate::wire::GuestRuntimeKind;
pub type DisposeReason = crate::wire::DisposeReason;
pub type FilesystemOperation = crate::wire::FilesystemOperation;
pub type GuestFilesystemOperation = crate::wire::GuestFilesystemOperation;
pub type PermissionMode = crate::wire::PermissionMode;
pub type FsPermissionRule = crate::wire::FsPermissionRule;
pub type PatternPermissionRule = crate::wire::PatternPermissionRule;
pub type FsPermissionRuleSet = crate::wire::FsPermissionRuleSet;
pub type PatternPermissionRuleSet = crate::wire::PatternPermissionRuleSet;
pub type FsPermissionScope = crate::wire::FsPermissionScope;
pub type PatternPermissionScope = crate::wire::PatternPermissionScope;
pub type PermissionsPolicy = crate::wire::PermissionsPolicy;
pub type RootFilesystemEntryKind = crate::wire::RootFilesystemEntryKind;
pub type RootFilesystemMode = crate::wire::RootFilesystemMode;
pub type RootFilesystemLowerDescriptor = crate::wire::RootFilesystemLowerDescriptor;
pub type SnapshotRootFilesystemLower = crate::wire::SnapshotRootFilesystemLower;
pub type StreamChannel = crate::wire::StreamChannel;
pub type VmLifecycleState = crate::wire::VmLifecycleState;
pub type AuthenticateRequest = crate::wire::AuthenticateRequest;
pub type OpenSessionRequest = crate::wire::OpenSessionRequest;
pub type CreateVmRequest = crate::wire::CreateVmRequest;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DisposeVmRequest {
pub reason: DisposeReason,
}
pub type BootstrapRootFilesystemRequest = crate::wire::BootstrapRootFilesystemRequest;
pub type RootFilesystemDescriptor = crate::wire::RootFilesystemDescriptor;
pub type RootFilesystemEntryEncoding = crate::wire::RootFilesystemEntryEncoding;
pub type RootFilesystemEntry = crate::wire::RootFilesystemEntry;
pub type ConfigureVmRequest = crate::wire::ConfigureVmRequest;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct CreateLayerRequest {}
pub type SealLayerRequest = crate::wire::SealLayerRequest;
pub type ImportSnapshotRequest = crate::wire::ImportSnapshotRequest;
pub type ExportSnapshotRequest = crate::wire::ExportSnapshotRequest;
pub type CreateOverlayRequest = crate::wire::CreateOverlayRequest;
pub type GuestFilesystemCallRequest = crate::wire::GuestFilesystemCallRequest;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct SnapshotRootFilesystemRequest {}
pub type MountDescriptor = crate::wire::MountDescriptor;
pub type MountPluginDescriptor = crate::wire::MountPluginDescriptor;
pub type SoftwareDescriptor = crate::wire::SoftwareDescriptor;
pub type ProjectedModuleDescriptor = crate::wire::ProjectedModuleDescriptor;
pub type WasmPermissionTier = crate::wire::WasmPermissionTier;
pub type ExecuteRequest = crate::wire::ExecuteRequest;
pub type WriteStdinRequest = crate::wire::WriteStdinRequest;
pub type CloseStdinRequest = crate::wire::CloseStdinRequest;
pub type KillProcessRequest = crate::wire::KillProcessRequest;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct GetProcessSnapshotRequest {}
pub type FindListenerRequest = crate::wire::FindListenerRequest;
pub type FindBoundUdpRequest = crate::wire::FindBoundUdpRequest;
pub type VmFetchRequest = crate::wire::VmFetchRequest;
pub type GetSignalStateRequest = crate::wire::GetSignalStateRequest;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct GetZombieTimerCountRequest {}
pub type HostFilesystemCallRequest = crate::wire::HostFilesystemCallRequest;
pub type PersistenceLoadRequest = crate::wire::PersistenceLoadRequest;
pub type PersistenceFlushRequest = crate::wire::PersistenceFlushRequest;
pub type RegisterHostCallbacksRequest = crate::wire::RegisterHostCallbacksRequest;
pub type RegisteredHostCallbackDefinition = crate::wire::RegisteredHostCallbackDefinition;
pub type RegisteredHostCallbackExample = crate::wire::RegisteredHostCallbackExample;
pub type HostCallbackRequest = crate::wire::HostCallbackRequest;
pub type JsBridgeCallRequest = crate::wire::JsBridgeCallRequest;
pub type AuthenticatedResponse = crate::wire::AuthenticatedResponse;
pub type SessionOpenedResponse = crate::wire::SessionOpenedResponse;
pub type VmCreatedResponse = crate::wire::VmCreatedResponse;
pub type VmDisposedResponse = crate::wire::VmDisposedResponse;
pub type RootFilesystemBootstrappedResponse = crate::wire::RootFilesystemBootstrappedResponse;
pub type VmConfiguredResponse = crate::wire::VmConfiguredResponse;
pub type HostCallbacksRegisteredResponse = crate::wire::HostCallbacksRegisteredResponse;
pub type GuestFilesystemStat = crate::wire::GuestFilesystemStat;
pub type GuestFilesystemResultResponse = crate::wire::GuestFilesystemResultResponse;
pub type RootFilesystemSnapshotResponse = crate::wire::RootFilesystemSnapshotResponse;
pub type LayerCreatedResponse = crate::wire::LayerCreatedResponse;
pub type LayerSealedResponse = crate::wire::LayerSealedResponse;
pub type SnapshotImportedResponse = crate::wire::SnapshotImportedResponse;
pub type SnapshotExportedResponse = crate::wire::SnapshotExportedResponse;
pub type OverlayCreatedResponse = crate::wire::OverlayCreatedResponse;
pub type ProcessStartedResponse = crate::wire::ProcessStartedResponse;
pub type StdinWrittenResponse = crate::wire::StdinWrittenResponse;
pub type StdinClosedResponse = crate::wire::StdinClosedResponse;
pub type ProcessKilledResponse = crate::wire::ProcessKilledResponse;
pub type ProcessSnapshotStatus = crate::wire::ProcessSnapshotStatus;
pub type ProcessSnapshotEntry = crate::wire::ProcessSnapshotEntry;
pub type ProcessSnapshotResponse = crate::wire::ProcessSnapshotResponse;
pub type SocketStateEntry = crate::wire::SocketStateEntry;
pub type ListenerSnapshotResponse = crate::wire::ListenerSnapshotResponse;
pub type BoundUdpSnapshotResponse = crate::wire::BoundUdpSnapshotResponse;
pub type VmFetchResponse = crate::wire::VmFetchResponse;
pub type SignalDispositionAction = crate::wire::SignalDispositionAction;
pub type SignalHandlerRegistration = crate::wire::SignalHandlerRegistration;
pub type SignalStateResponse = crate::wire::SignalStateResponse;
pub type ZombieTimerCountResponse = crate::wire::ZombieTimerCountResponse;
pub type FilesystemResultResponse = crate::wire::FilesystemResultResponse;
pub type PermissionDecisionResponse = crate::wire::PermissionDecisionResponse;
pub type PersistenceStateResponse = crate::wire::PersistenceStateResponse;
pub type PersistenceFlushedResponse = crate::wire::PersistenceFlushedResponse;
pub type HostCallbackResultResponse = crate::wire::HostCallbackResultResponse;
pub type JsBridgeResultResponse = crate::wire::JsBridgeResultResponse;
pub type RejectedResponse = crate::wire::RejectedResponse;
pub type VmLifecycleEvent = crate::wire::VmLifecycleEvent;
pub type ProcessOutputEvent = crate::wire::ProcessOutputEvent;
pub type ProcessExitedEvent = crate::wire::ProcessExitedEvent;
pub type StructuredEvent = crate::wire::StructuredEvent;
impl_bare_newtype_union_enum!(
ProtocolFrame,
JsonProtocolFrame,
#[serde(tag = "frame_type", rename_all = "snake_case")]
{
Request(RequestFrame) = 0,
Response(ResponseFrame) = 1,
Event(EventFrame) = 2,
SidecarRequest(SidecarRequestFrame) = 3,
SidecarResponse(SidecarResponseFrame) = 4,
}
);
impl_bare_newtype_union_enum!(
RequestPayload,
JsonRequestPayload,
#[serde(tag = "type", rename_all = "snake_case")]
{
Authenticate(AuthenticateRequest) = 0,
OpenSession(OpenSessionRequest) = 1,
CreateVm(CreateVmRequest) = 2,
DisposeVm(DisposeVmRequest) = 3,
BootstrapRootFilesystem(BootstrapRootFilesystemRequest) = 4,
ConfigureVm(ConfigureVmRequest) = 5,
RegisterHostCallbacks(RegisterHostCallbacksRequest) = 6,
CreateLayer(CreateLayerRequest) = 7,
SealLayer(SealLayerRequest) = 8,
ImportSnapshot(ImportSnapshotRequest) = 9,
ExportSnapshot(ExportSnapshotRequest) = 10,
CreateOverlay(CreateOverlayRequest) = 11,
GuestFilesystemCall(GuestFilesystemCallRequest) = 12,
SnapshotRootFilesystem(SnapshotRootFilesystemRequest) = 13,
Execute(ExecuteRequest) = 14,
WriteStdin(WriteStdinRequest) = 15,
CloseStdin(CloseStdinRequest) = 16,
KillProcess(KillProcessRequest) = 17,
GetProcessSnapshot(GetProcessSnapshotRequest) = 18,
FindListener(FindListenerRequest) = 19,
FindBoundUdp(FindBoundUdpRequest) = 20,
GetSignalState(GetSignalStateRequest) = 21,
GetZombieTimerCount(GetZombieTimerCountRequest) = 22,
HostFilesystemCall(HostFilesystemCallRequest) = 23,
PersistenceLoad(PersistenceLoadRequest) = 24,
PersistenceFlush(PersistenceFlushRequest) = 25,
VmFetch(VmFetchRequest) = 26,
Ext(ExtEnvelope) = 27,
}
);
impl_bare_newtype_union_enum!(
ResponsePayload,
JsonResponsePayload,
#[serde(tag = "type", rename_all = "snake_case")]
{
Authenticated(AuthenticatedResponse) = 0,
SessionOpened(SessionOpenedResponse) = 1,
VmCreated(VmCreatedResponse) = 2,
VmDisposed(VmDisposedResponse) = 3,
RootFilesystemBootstrapped(RootFilesystemBootstrappedResponse) = 4,
VmConfigured(VmConfiguredResponse) = 5,
HostCallbacksRegistered(HostCallbacksRegisteredResponse) = 6,
LayerCreated(LayerCreatedResponse) = 7,
LayerSealed(LayerSealedResponse) = 8,
SnapshotImported(SnapshotImportedResponse) = 9,
SnapshotExported(SnapshotExportedResponse) = 10,
OverlayCreated(OverlayCreatedResponse) = 11,
GuestFilesystemResult(GuestFilesystemResultResponse) = 12,
RootFilesystemSnapshot(RootFilesystemSnapshotResponse) = 13,
ProcessStarted(ProcessStartedResponse) = 14,
StdinWritten(StdinWrittenResponse) = 15,
StdinClosed(StdinClosedResponse) = 16,
ProcessKilled(ProcessKilledResponse) = 17,
ProcessSnapshot(ProcessSnapshotResponse) = 18,
ListenerSnapshot(ListenerSnapshotResponse) = 19,
BoundUdpSnapshot(BoundUdpSnapshotResponse) = 20,
SignalState(SignalStateResponse) = 21,
ZombieTimerCount(ZombieTimerCountResponse) = 22,
FilesystemResult(FilesystemResultResponse) = 23,
PermissionDecision(PermissionDecisionResponse) = 24,
PersistenceState(PersistenceStateResponse) = 25,
PersistenceFlushed(PersistenceFlushedResponse) = 26,
Rejected(RejectedResponse) = 27,
VmFetchResult(VmFetchResponse) = 28,
ExtResult(ExtEnvelope) = 29,
}
);
impl_bare_newtype_union_enum!(
SidecarRequestPayload,
JsonSidecarRequestPayload,
#[serde(tag = "type", rename_all = "snake_case")]
{
HostCallback(HostCallbackRequest) = 0,
JsBridgeCall(JsBridgeCallRequest) = 1,
Ext(ExtEnvelope) = 2,
}
);
impl_bare_newtype_union_enum!(
SidecarResponsePayload,
JsonSidecarResponsePayload,
#[allow(clippy::enum_variant_names)]
#[serde(tag = "type", rename_all = "snake_case")]
{
HostCallbackResult(HostCallbackResultResponse) = 0,
JsBridgeResult(JsBridgeResultResponse) = 1,
ExtResult(ExtEnvelope) = 2,
}
);
impl_bare_newtype_union_enum!(
EventPayload,
JsonEventPayload,
#[serde(tag = "type", rename_all = "snake_case")]
{
VmLifecycle(VmLifecycleEvent) = 0,
ProcessOutput(ProcessOutputEvent) = 1,
ProcessExited(ProcessExitedEvent) = 2,
Structured(StructuredEvent) = 3,
Ext(ExtEnvelope) = 4,
}
);
fn serialize_payload(
frame: &ProtocolFrame,
payload_codec: NativePayloadCodec,
) -> Result<Vec<u8>, ProtocolCodecError> {
match payload_codec {
NativePayloadCodec::Json => serde_json::to_vec(frame)
.map_err(|error| ProtocolCodecError::SerializeFailure(error.to_string())),
NativePayloadCodec::Bare => serde_bare::to_vec(&to_generated_protocol_frame(frame)?)
.map_err(|error| ProtocolCodecError::SerializeFailure(error.to_string())),
}
}
fn deserialize_payload(
payload: &[u8],
payload_codec: NativePayloadCodec,
) -> Result<ProtocolFrame, ProtocolCodecError> {
match payload_codec {
NativePayloadCodec::Json => serde_json::from_slice(payload)
.map_err(|error| ProtocolCodecError::DeserializeFailure(error.to_string())),
NativePayloadCodec::Bare => {
let frame: generated_protocol::ProtocolFrame = serde_bare::from_slice(payload)
.map_err(|error| ProtocolCodecError::DeserializeFailure(error.to_string()))?;
from_generated_protocol_frame(frame)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NativePayloadCodec {
Json,
Bare,
}
impl NativePayloadCodec {
pub fn sniff(payload: &[u8]) -> Self {
match payload.first() {
Some(b'{') => Self::Json,
_ => Self::Bare,
}
}
pub fn alternate(self) -> Self {
match self {
Self::Json => Self::Bare,
Self::Bare => Self::Json,
}
}
}
#[derive(Debug, Clone)]
pub struct NativeFrameCodec {
max_frame_bytes: usize,
payload_codec: NativePayloadCodec,
}
impl NativeFrameCodec {
pub fn new(max_frame_bytes: usize) -> Self {
Self::with_payload_codec(max_frame_bytes, NativePayloadCodec::Json)
}
pub fn with_payload_codec(max_frame_bytes: usize, payload_codec: NativePayloadCodec) -> Self {
Self {
max_frame_bytes,
payload_codec,
}
}
pub fn max_frame_bytes(&self) -> usize {
self.max_frame_bytes
}
pub fn payload_codec(&self) -> NativePayloadCodec {
self.payload_codec
}
pub fn encode(&self, frame: &ProtocolFrame) -> Result<Vec<u8>, ProtocolCodecError> {
self.encode_with_codec(frame, self.payload_codec)
}
pub fn encode_with_codec(
&self,
frame: &ProtocolFrame,
payload_codec: NativePayloadCodec,
) -> Result<Vec<u8>, ProtocolCodecError> {
validate_frame(frame)?;
let payload = serialize_payload(frame, payload_codec)?;
if payload.len() > self.max_frame_bytes {
return Err(ProtocolCodecError::FrameTooLarge {
size: payload.len(),
max: self.max_frame_bytes,
});
}
let length =
u32::try_from(payload.len()).map_err(|_| ProtocolCodecError::FrameTooLarge {
size: payload.len(),
max: u32::MAX as usize,
})?;
let mut encoded = Vec::with_capacity(4 + payload.len());
encoded.extend_from_slice(&length.to_be_bytes());
encoded.extend_from_slice(&payload);
Ok(encoded)
}
pub fn decode(&self, bytes: &[u8]) -> Result<ProtocolFrame, ProtocolCodecError> {
self.decode_detected(bytes).map(|(frame, _)| frame)
}
pub fn decode_with_codec(
&self,
bytes: &[u8],
payload_codec: NativePayloadCodec,
) -> Result<ProtocolFrame, ProtocolCodecError> {
let payload = self.checked_payload(bytes)?;
let frame = deserialize_payload(payload, payload_codec)?;
validate_frame(&frame)?;
Ok(frame)
}
pub fn decode_detected(
&self,
bytes: &[u8],
) -> Result<(ProtocolFrame, NativePayloadCodec), ProtocolCodecError> {
let payload = self.checked_payload(bytes)?;
let primary = NativePayloadCodec::sniff(payload);
match deserialize_payload(payload, primary) {
Ok(frame) => {
validate_frame(&frame)?;
Ok((frame, primary))
}
Err(primary_error) => {
let alternate = primary.alternate();
let frame = deserialize_payload(payload, alternate).map_err(|_| primary_error)?;
validate_frame(&frame)?;
Ok((frame, alternate))
}
}
}
fn checked_payload<'a>(&self, bytes: &'a [u8]) -> Result<&'a [u8], ProtocolCodecError> {
if bytes.len() < 4 {
return Err(ProtocolCodecError::TruncatedFrame {
actual: bytes.len(),
});
}
let declared =
u32::from_be_bytes(bytes[..4].try_into().expect("length prefix is four bytes"))
as usize;
if declared > self.max_frame_bytes {
return Err(ProtocolCodecError::FrameTooLarge {
size: declared,
max: self.max_frame_bytes,
});
}
let actual = bytes.len() - 4;
if declared != actual {
return Err(ProtocolCodecError::LengthPrefixMismatch { declared, actual });
}
Ok(&bytes[4..])
}
}
impl Default for NativeFrameCodec {
fn default() -> Self {
Self::new(DEFAULT_MAX_FRAME_BYTES)
}
}
#[derive(Debug)]
pub struct ResponseTracker {
pending: HashMap<RequestId, PendingRequest>,
completed: HashSet<RequestId>,
completed_order: VecDeque<RequestId>,
completed_cap: usize,
}
#[derive(Debug)]
pub struct SidecarResponseTracker {
pending: HashMap<RequestId, PendingSidecarRequest>,
completed: HashSet<RequestId>,
completed_order: VecDeque<RequestId>,
completed_cap: usize,
}
impl ResponseTracker {
pub fn with_completed_cap(completed_cap: usize) -> Self {
Self {
pending: HashMap::new(),
completed: HashSet::new(),
completed_order: VecDeque::new(),
completed_cap: completed_cap.max(1),
}
}
pub fn completed_count(&self) -> usize {
self.completed.len()
}
pub fn register_request(&mut self, request: &RequestFrame) -> Result<(), ResponseTrackerError> {
if self.pending.contains_key(&request.request_id)
|| self.completed.contains(&request.request_id)
{
return Err(ResponseTrackerError::DuplicateRequestId {
request_id: request.request_id,
});
}
self.pending.insert(
request.request_id,
PendingRequest {
ownership: request.ownership.clone(),
expected_response: request.payload.expected_response(),
},
);
Ok(())
}
pub fn accept_response(
&mut self,
response: &ResponseFrame,
) -> Result<(), ResponseTrackerError> {
if self.completed.contains(&response.request_id) {
return Err(ResponseTrackerError::DuplicateResponse {
request_id: response.request_id,
});
}
let pending = self.pending.get(&response.request_id).ok_or(
ResponseTrackerError::UnmatchedResponse {
request_id: response.request_id,
},
)?;
if pending.ownership != response.ownership {
return Err(ResponseTrackerError::OwnershipMismatch {
request_id: response.request_id,
expected: Box::new(pending.ownership.clone()),
actual: Box::new(response.ownership.clone()),
});
}
if !pending.expected_response.matches(&response.payload) {
return Err(ResponseTrackerError::ResponseKindMismatch {
request_id: response.request_id,
expected: pending.expected_response.as_str().to_string(),
actual: response.payload.kind_name().to_string(),
});
}
self.pending
.remove(&response.request_id)
.expect("pending response should still exist after validation");
self.completed.insert(response.request_id);
self.completed_order.push_back(response.request_id);
while self.completed.len() > self.completed_cap {
if let Some(evicted) = self.completed_order.pop_front() {
self.completed.remove(&evicted);
}
}
Ok(())
}
}
impl Default for ResponseTracker {
fn default() -> Self {
Self::with_completed_cap(DEFAULT_COMPLETED_RESPONSE_CAP)
}
}
impl SidecarResponseTracker {
pub fn with_completed_cap(completed_cap: usize) -> Self {
Self {
pending: HashMap::new(),
completed: HashSet::new(),
completed_order: VecDeque::new(),
completed_cap: completed_cap.max(1),
}
}
pub fn pending_count(&self) -> usize {
self.pending.len()
}
pub fn completed_count(&self) -> usize {
self.completed.len()
}
pub fn register_request(
&mut self,
request: &SidecarRequestFrame,
) -> Result<(), SidecarResponseTrackerError> {
if self.pending.contains_key(&request.request_id)
|| self.completed.contains(&request.request_id)
{
return Err(SidecarResponseTrackerError::DuplicateRequestId {
request_id: request.request_id,
});
}
self.pending.insert(
request.request_id,
PendingSidecarRequest {
ownership: request.ownership.clone(),
expected_response: request.payload.expected_response(),
},
);
Ok(())
}
pub fn accept_response(
&mut self,
response: &SidecarResponseFrame,
) -> Result<(), SidecarResponseTrackerError> {
if self.completed.contains(&response.request_id) {
return Err(SidecarResponseTrackerError::DuplicateResponse {
request_id: response.request_id,
});
}
let pending = self.pending.get(&response.request_id).ok_or(
SidecarResponseTrackerError::UnmatchedResponse {
request_id: response.request_id,
},
)?;
if pending.ownership != response.ownership {
return Err(SidecarResponseTrackerError::OwnershipMismatch {
request_id: response.request_id,
expected: Box::new(pending.ownership.clone()),
actual: Box::new(response.ownership.clone()),
});
}
if !pending.expected_response.matches(&response.payload) {
return Err(SidecarResponseTrackerError::ResponseKindMismatch {
request_id: response.request_id,
expected: pending.expected_response.as_str().to_string(),
actual: response.payload.kind_name().to_string(),
});
}
self.pending
.remove(&response.request_id)
.expect("pending sidecar response should still exist after validation");
self.completed.insert(response.request_id);
self.completed_order.push_back(response.request_id);
while self.completed.len() > self.completed_cap {
if let Some(evicted) = self.completed_order.pop_front() {
self.completed.remove(&evicted);
}
}
Ok(())
}
}
impl Default for SidecarResponseTracker {
fn default() -> Self {
Self::with_completed_cap(DEFAULT_COMPLETED_RESPONSE_CAP)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResponseTrackerError {
DuplicateRequestId {
request_id: RequestId,
},
UnmatchedResponse {
request_id: RequestId,
},
DuplicateResponse {
request_id: RequestId,
},
OwnershipMismatch {
request_id: RequestId,
expected: Box<OwnershipScope>,
actual: Box<OwnershipScope>,
},
ResponseKindMismatch {
request_id: RequestId,
expected: String,
actual: String,
},
}
impl fmt::Display for ResponseTrackerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DuplicateRequestId { request_id } => {
write!(f, "request id {request_id} is already tracked")
}
Self::UnmatchedResponse { request_id } => {
write!(
f,
"response id {request_id} does not match any pending request"
)
}
Self::DuplicateResponse { request_id } => {
write!(f, "response id {request_id} has already been completed")
}
Self::OwnershipMismatch {
request_id,
expected,
actual,
} => write!(
f,
"response id {request_id} used ownership {:?}, expected {:?}",
actual, expected
),
Self::ResponseKindMismatch {
request_id,
expected,
actual,
} => write!(
f,
"response id {request_id} carried {actual}, expected {expected}",
),
}
}
}
impl Error for ResponseTrackerError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SidecarResponseTrackerError {
DuplicateRequestId {
request_id: RequestId,
},
UnmatchedResponse {
request_id: RequestId,
},
DuplicateResponse {
request_id: RequestId,
},
OwnershipMismatch {
request_id: RequestId,
expected: Box<OwnershipScope>,
actual: Box<OwnershipScope>,
},
ResponseKindMismatch {
request_id: RequestId,
expected: String,
actual: String,
},
}
impl fmt::Display for SidecarResponseTrackerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DuplicateRequestId { request_id } => {
write!(f, "sidecar request id {request_id} is already tracked")
}
Self::UnmatchedResponse { request_id } => {
write!(
f,
"sidecar response id {request_id} does not match any pending request"
)
}
Self::DuplicateResponse { request_id } => {
write!(
f,
"sidecar response id {request_id} has already been completed"
)
}
Self::OwnershipMismatch {
request_id,
expected,
actual,
} => write!(
f,
"sidecar response id {request_id} used ownership {:?}, expected {:?}",
actual, expected
),
Self::ResponseKindMismatch {
request_id,
expected,
actual,
} => write!(
f,
"sidecar response id {request_id} carried {actual}, expected {expected}",
),
}
}
}
impl Error for SidecarResponseTrackerError {}
#[derive(Debug, Clone, PartialEq, Eq)]
struct PendingRequest {
ownership: OwnershipScope,
expected_response: ExpectedResponseKind,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct PendingSidecarRequest {
ownership: OwnershipScope,
expected_response: ExpectedSidecarResponseKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ExpectedResponseKind {
Authenticated,
SessionOpened,
VmCreated,
VmDisposed,
RootFilesystemBootstrapped,
VmConfigured,
HostCallbacksRegistered,
LayerCreated,
LayerSealed,
SnapshotImported,
SnapshotExported,
OverlayCreated,
GuestFilesystemResult,
RootFilesystemSnapshot,
ProcessStarted,
StdinWritten,
StdinClosed,
ProcessKilled,
ProcessSnapshot,
ListenerSnapshot,
BoundUdpSnapshot,
VmFetchResult,
SignalState,
ZombieTimerCount,
FilesystemResult,
#[allow(dead_code)]
PermissionDecision,
PersistenceState,
PersistenceFlushed,
ExtResult,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ExpectedSidecarResponseKind {
HostCallback,
JsBridge,
Ext,
}
impl ExpectedResponseKind {
fn as_str(self) -> &'static str {
match self {
Self::Authenticated => "authenticated",
Self::SessionOpened => "session_opened",
Self::VmCreated => "vm_created",
Self::VmDisposed => "vm_disposed",
Self::RootFilesystemBootstrapped => "root_filesystem_bootstrapped",
Self::VmConfigured => "vm_configured",
Self::HostCallbacksRegistered => "host_callbacks_registered",
Self::LayerCreated => "layer_created",
Self::LayerSealed => "layer_sealed",
Self::SnapshotImported => "snapshot_imported",
Self::SnapshotExported => "snapshot_exported",
Self::OverlayCreated => "overlay_created",
Self::GuestFilesystemResult => "guest_filesystem_result",
Self::RootFilesystemSnapshot => "root_filesystem_snapshot",
Self::ProcessStarted => "process_started",
Self::StdinWritten => "stdin_written",
Self::StdinClosed => "stdin_closed",
Self::ProcessKilled => "process_killed",
Self::ProcessSnapshot => "process_snapshot",
Self::ListenerSnapshot => "listener_snapshot",
Self::BoundUdpSnapshot => "bound_udp_snapshot",
Self::VmFetchResult => "vm_fetch_result",
Self::SignalState => "signal_state",
Self::ZombieTimerCount => "zombie_timer_count",
Self::FilesystemResult => "filesystem_result",
Self::PermissionDecision => "permission_decision",
Self::PersistenceState => "persistence_state",
Self::PersistenceFlushed => "persistence_flushed",
Self::ExtResult => "ext_result",
}
}
fn matches(self, payload: &ResponsePayload) -> bool {
match payload {
ResponsePayload::Rejected(_) => true,
_ => payload.kind_name() == self.as_str(),
}
}
}
impl ExpectedSidecarResponseKind {
fn as_str(self) -> &'static str {
match self {
Self::HostCallback => "host_callback_result",
Self::JsBridge => "js_bridge_result",
Self::Ext => "ext_result",
}
}
fn matches(self, payload: &SidecarResponsePayload) -> bool {
payload.kind_name() == self.as_str()
}
}
impl RequestPayload {
fn ownership_requirement(&self) -> OwnershipRequirement {
match self {
Self::Authenticate(_) | Self::OpenSession(_) => OwnershipRequirement::Connection,
Self::CreateVm(_) | Self::PersistenceLoad(_) | Self::PersistenceFlush(_) => {
OwnershipRequirement::Session
}
Self::DisposeVm(_)
| Self::BootstrapRootFilesystem(_)
| Self::ConfigureVm(_)
| Self::RegisterHostCallbacks(_)
| Self::CreateLayer(_)
| Self::SealLayer(_)
| Self::ImportSnapshot(_)
| Self::ExportSnapshot(_)
| Self::CreateOverlay(_)
| Self::GuestFilesystemCall(_)
| Self::SnapshotRootFilesystem(_)
| Self::Execute(_)
| Self::WriteStdin(_)
| Self::CloseStdin(_)
| Self::KillProcess(_)
| Self::GetProcessSnapshot(_)
| Self::FindListener(_)
| Self::FindBoundUdp(_)
| Self::VmFetch(_)
| Self::GetSignalState(_)
| Self::GetZombieTimerCount(_)
| Self::HostFilesystemCall(_) => OwnershipRequirement::Vm,
Self::Ext(_) => OwnershipRequirement::Any,
}
}
fn expected_response(&self) -> ExpectedResponseKind {
match self {
Self::Authenticate(_) => ExpectedResponseKind::Authenticated,
Self::OpenSession(_) => ExpectedResponseKind::SessionOpened,
Self::CreateVm(_) => ExpectedResponseKind::VmCreated,
Self::DisposeVm(_) => ExpectedResponseKind::VmDisposed,
Self::BootstrapRootFilesystem(_) => ExpectedResponseKind::RootFilesystemBootstrapped,
Self::ConfigureVm(_) => ExpectedResponseKind::VmConfigured,
Self::RegisterHostCallbacks(_) => ExpectedResponseKind::HostCallbacksRegistered,
Self::CreateLayer(_) => ExpectedResponseKind::LayerCreated,
Self::SealLayer(_) => ExpectedResponseKind::LayerSealed,
Self::ImportSnapshot(_) => ExpectedResponseKind::SnapshotImported,
Self::ExportSnapshot(_) => ExpectedResponseKind::SnapshotExported,
Self::CreateOverlay(_) => ExpectedResponseKind::OverlayCreated,
Self::GuestFilesystemCall(_) => ExpectedResponseKind::GuestFilesystemResult,
Self::SnapshotRootFilesystem(_) => ExpectedResponseKind::RootFilesystemSnapshot,
Self::Execute(_) => ExpectedResponseKind::ProcessStarted,
Self::WriteStdin(_) => ExpectedResponseKind::StdinWritten,
Self::CloseStdin(_) => ExpectedResponseKind::StdinClosed,
Self::KillProcess(_) => ExpectedResponseKind::ProcessKilled,
Self::GetProcessSnapshot(_) => ExpectedResponseKind::ProcessSnapshot,
Self::FindListener(_) => ExpectedResponseKind::ListenerSnapshot,
Self::FindBoundUdp(_) => ExpectedResponseKind::BoundUdpSnapshot,
Self::VmFetch(_) => ExpectedResponseKind::VmFetchResult,
Self::GetSignalState(_) => ExpectedResponseKind::SignalState,
Self::GetZombieTimerCount(_) => ExpectedResponseKind::ZombieTimerCount,
Self::HostFilesystemCall(_) => ExpectedResponseKind::FilesystemResult,
Self::PersistenceLoad(_) => ExpectedResponseKind::PersistenceState,
Self::PersistenceFlush(_) => ExpectedResponseKind::PersistenceFlushed,
Self::Ext(_) => ExpectedResponseKind::ExtResult,
}
}
}
impl SidecarRequestPayload {
fn ownership_requirement(&self) -> OwnershipRequirement {
OwnershipRequirement::Vm
}
fn expected_response(&self) -> ExpectedSidecarResponseKind {
match self {
Self::HostCallback(_) => ExpectedSidecarResponseKind::HostCallback,
Self::JsBridgeCall(_) => ExpectedSidecarResponseKind::JsBridge,
Self::Ext(_) => ExpectedSidecarResponseKind::Ext,
}
}
}
impl ResponsePayload {
fn ownership_requirement(&self) -> OwnershipRequirement {
match self {
Self::Authenticated(_) | Self::SessionOpened(_) => OwnershipRequirement::Connection,
Self::VmCreated(_) | Self::PersistenceState(_) | Self::PersistenceFlushed(_) => {
OwnershipRequirement::Session
}
Self::Rejected(_) => OwnershipRequirement::Any,
Self::VmDisposed(_)
| Self::RootFilesystemBootstrapped(_)
| Self::VmConfigured(_)
| Self::HostCallbacksRegistered(_)
| Self::LayerCreated(_)
| Self::LayerSealed(_)
| Self::SnapshotImported(_)
| Self::SnapshotExported(_)
| Self::OverlayCreated(_)
| Self::GuestFilesystemResult(_)
| Self::RootFilesystemSnapshot(_)
| Self::ProcessStarted(_)
| Self::StdinWritten(_)
| Self::StdinClosed(_)
| Self::ProcessKilled(_)
| Self::ProcessSnapshot(_)
| Self::ListenerSnapshot(_)
| Self::BoundUdpSnapshot(_)
| Self::VmFetchResult(_)
| Self::SignalState(_)
| Self::ZombieTimerCount(_)
| Self::FilesystemResult(_)
| Self::PermissionDecision(_) => OwnershipRequirement::Vm,
Self::ExtResult(_) => OwnershipRequirement::Any,
}
}
fn kind_name(&self) -> &'static str {
match self {
Self::Authenticated(_) => "authenticated",
Self::SessionOpened(_) => "session_opened",
Self::VmCreated(_) => "vm_created",
Self::VmDisposed(_) => "vm_disposed",
Self::RootFilesystemBootstrapped(_) => "root_filesystem_bootstrapped",
Self::VmConfigured(_) => "vm_configured",
Self::HostCallbacksRegistered(_) => "host_callbacks_registered",
Self::LayerCreated(_) => "layer_created",
Self::LayerSealed(_) => "layer_sealed",
Self::SnapshotImported(_) => "snapshot_imported",
Self::SnapshotExported(_) => "snapshot_exported",
Self::OverlayCreated(_) => "overlay_created",
Self::GuestFilesystemResult(_) => "guest_filesystem_result",
Self::RootFilesystemSnapshot(_) => "root_filesystem_snapshot",
Self::ProcessStarted(_) => "process_started",
Self::StdinWritten(_) => "stdin_written",
Self::StdinClosed(_) => "stdin_closed",
Self::ProcessKilled(_) => "process_killed",
Self::ProcessSnapshot(_) => "process_snapshot",
Self::ListenerSnapshot(_) => "listener_snapshot",
Self::BoundUdpSnapshot(_) => "bound_udp_snapshot",
Self::VmFetchResult(_) => "vm_fetch_result",
Self::SignalState(_) => "signal_state",
Self::ZombieTimerCount(_) => "zombie_timer_count",
Self::FilesystemResult(_) => "filesystem_result",
Self::PermissionDecision(_) => "permission_decision",
Self::PersistenceState(_) => "persistence_state",
Self::PersistenceFlushed(_) => "persistence_flushed",
Self::Rejected(_) => "rejected",
Self::ExtResult(_) => "ext_result",
}
}
}
impl SidecarResponsePayload {
fn ownership_requirement(&self) -> OwnershipRequirement {
OwnershipRequirement::Vm
}
fn kind_name(&self) -> &'static str {
match self {
Self::HostCallbackResult(_) => "host_callback_result",
Self::JsBridgeResult(_) => "js_bridge_result",
Self::ExtResult(_) => "ext_result",
}
}
}
impl EventPayload {
fn ownership_requirement(&self) -> OwnershipRequirement {
match self {
Self::Structured(_) => OwnershipRequirement::SessionOrVm,
Self::VmLifecycle(_) | Self::ProcessOutput(_) | Self::ProcessExited(_) => {
OwnershipRequirement::Vm
}
Self::Ext(_) => OwnershipRequirement::Any,
}
}
}
pub fn validate_frame(frame: &ProtocolFrame) -> Result<(), ProtocolCodecError> {
match frame {
ProtocolFrame::Request(request) => validate_request(request),
ProtocolFrame::Response(response) => validate_response(response),
ProtocolFrame::Event(event) => validate_event(event),
ProtocolFrame::SidecarRequest(request) => validate_sidecar_request(request),
ProtocolFrame::SidecarResponse(response) => validate_sidecar_response(response),
}
}
fn validate_request(request: &RequestFrame) -> Result<(), ProtocolCodecError> {
validate_schema(&request.schema)?;
validate_request_id_direction(request.request_id, RequestDirection::Host)?;
validate_ownership(&request.ownership)?;
validate_requirement(request.payload.ownership_requirement(), &request.ownership)?;
if let RequestPayload::Authenticate(authenticate) = &request.payload {
if authenticate.auth_token.is_empty() {
return Err(ProtocolCodecError::EmptyAuthToken);
}
}
Ok(())
}
fn validate_response(response: &ResponseFrame) -> Result<(), ProtocolCodecError> {
validate_schema(&response.schema)?;
validate_request_id_direction(response.request_id, RequestDirection::Host)?;
validate_ownership(&response.ownership)?;
validate_requirement(
response.payload.ownership_requirement(),
&response.ownership,
)?;
Ok(())
}
fn validate_sidecar_request(request: &SidecarRequestFrame) -> Result<(), ProtocolCodecError> {
validate_schema(&request.schema)?;
validate_request_id_direction(request.request_id, RequestDirection::Sidecar)?;
validate_ownership(&request.ownership)?;
validate_requirement(request.payload.ownership_requirement(), &request.ownership)?;
Ok(())
}
fn validate_sidecar_response(response: &SidecarResponseFrame) -> Result<(), ProtocolCodecError> {
validate_schema(&response.schema)?;
validate_request_id_direction(response.request_id, RequestDirection::Sidecar)?;
validate_ownership(&response.ownership)?;
validate_requirement(
response.payload.ownership_requirement(),
&response.ownership,
)?;
Ok(())
}
fn validate_event(event: &EventFrame) -> Result<(), ProtocolCodecError> {
validate_schema(&event.schema)?;
validate_ownership(&event.ownership)?;
validate_requirement(event.payload.ownership_requirement(), &event.ownership)?;
Ok(())
}
fn validate_schema(schema: &ProtocolSchema) -> Result<(), ProtocolCodecError> {
if schema.name != PROTOCOL_NAME || schema.version != PROTOCOL_VERSION {
return Err(ProtocolCodecError::UnsupportedSchema {
name: schema.name.clone(),
version: schema.version,
});
}
Ok(())
}
fn validate_ownership(ownership: &OwnershipScope) -> Result<(), ProtocolCodecError> {
match ownership {
OwnershipScope::ConnectionOwnership(inner) => {
validate_non_empty("connection_id", &inner.connection_id)
}
OwnershipScope::SessionOwnership(inner) => {
validate_non_empty("connection_id", &inner.connection_id)?;
validate_non_empty("session_id", &inner.session_id)
}
OwnershipScope::VmOwnership(inner) => {
validate_non_empty("connection_id", &inner.connection_id)?;
validate_non_empty("session_id", &inner.session_id)?;
validate_non_empty("vm_id", &inner.vm_id)
}
}
}
fn validate_non_empty(field: &'static str, value: &str) -> Result<(), ProtocolCodecError> {
if value.is_empty() {
return Err(ProtocolCodecError::EmptyOwnershipField { field });
}
Ok(())
}
fn validate_request_id_direction(
request_id: RequestId,
direction: RequestDirection,
) -> Result<(), ProtocolCodecError> {
if request_id == 0 {
return Err(ProtocolCodecError::InvalidRequestId);
}
let matches_direction = match direction {
RequestDirection::Host => request_id > 0,
RequestDirection::Sidecar => request_id < 0,
};
if matches_direction {
Ok(())
} else {
Err(ProtocolCodecError::InvalidRequestDirection {
request_id,
expected: direction,
})
}
}
fn validate_requirement(
required: OwnershipRequirement,
ownership: &OwnershipScope,
) -> Result<(), ProtocolCodecError> {
let actual = match ownership {
OwnershipScope::ConnectionOwnership(..) => OwnershipRequirement::Connection,
OwnershipScope::SessionOwnership(..) => OwnershipRequirement::Session,
OwnershipScope::VmOwnership(..) => OwnershipRequirement::Vm,
};
let valid = match required {
OwnershipRequirement::Any => true,
OwnershipRequirement::Connection => {
matches!(ownership, OwnershipScope::ConnectionOwnership(..))
}
OwnershipRequirement::Session => matches!(ownership, OwnershipScope::SessionOwnership(..)),
OwnershipRequirement::Vm => matches!(ownership, OwnershipScope::VmOwnership(..)),
OwnershipRequirement::SessionOrVm => {
matches!(
ownership,
OwnershipScope::SessionOwnership(..) | OwnershipScope::VmOwnership(..)
)
}
};
if valid {
Ok(())
} else {
Err(ProtocolCodecError::InvalidOwnershipScope { required, actual })
}
}
#[derive(Debug, Deserialize, Default)]
pub struct JavascriptChildProcessSpawnOptions {
#[serde(default)]
pub cwd: Option<String>,
#[serde(default)]
pub env: BTreeMap<String, String>,
#[serde(rename = "internalBootstrapEnv", default)]
pub internal_bootstrap_env: BTreeMap<String, String>,
#[serde(default)]
pub input: Option<Value>,
#[serde(default)]
pub shell: bool,
#[serde(default)]
pub detached: bool,
#[serde(default)]
pub stdio: Vec<String>,
#[serde(default)]
pub timeout: Option<u64>,
#[serde(rename = "killSignal", default)]
pub kill_signal: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptChildProcessSpawnRequest {
pub command: String,
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub options: JavascriptChildProcessSpawnOptions,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptNetConnectRequest {
#[serde(default)]
pub host: Option<String>,
#[serde(default)]
pub port: Option<u16>,
#[serde(default)]
pub path: Option<String>,
#[serde(rename = "localAddress", default)]
pub local_address: Option<String>,
#[serde(rename = "localPort", default)]
pub local_port: Option<u16>,
#[serde(rename = "localReservation", default)]
pub local_reservation: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptNetReserveTcpPortRequest {
#[serde(default)]
pub host: Option<String>,
#[serde(default)]
pub port: Option<u16>,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptNetListenRequest {
#[serde(default)]
pub host: Option<String>,
#[serde(default)]
pub port: Option<u16>,
#[serde(default)]
pub path: Option<String>,
#[serde(default)]
pub backlog: Option<u32>,
#[serde(rename = "localReservation", default)]
pub local_reservation: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptDgramCreateSocketRequest {
#[serde(rename = "type")]
pub socket_type: String,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptDgramBindRequest {
#[serde(default)]
pub address: Option<String>,
#[serde(default)]
pub port: u16,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptDgramSendRequest {
#[serde(default)]
pub address: Option<String>,
pub port: u16,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptDnsLookupRequest {
pub hostname: String,
#[serde(default)]
pub family: Option<u8>,
}
#[derive(Debug, Deserialize)]
pub struct JavascriptDnsResolveRequest {
pub hostname: String,
#[serde(default)]
pub rrtype: Option<String>,
}