Response
The sequence of channel responses the server sends, what ends it, and what a finish with nothing before it means.
[0] the file is at the destination
[1][error JSON …] it is not
- The sequence. Exactly one channel response, followed by the channel response finish. The server sends it after the read has ended and the proxy of the container named has answered. A channel response finish that no channel response precedes states that the proxy of the container of the scope did not serve the read.
- Transferred. The byte
0and nothing after it: the proxy of the container named answered that the file is at the destination, whole. - The error. After the byte
1, exactly one JSON value in the form defined on the volumes::list response page: the refusal of either proxy, the rule of the channel not met, or the server’s own error. The destination is as it was before the request.
The channel response is defined by diverge-sdk-rs/src/provider/endpoints/containers/tools/run/server/channel_response/transfer/frame.rs:
//! What a server's channel response frame carries on a transfer channel.
use std::fmt;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
use crate::shared::containers::transfer;
use crate::shared::error::Error;
/// The file landed in the other container, or it did not.
///
/// A payload leads with one byte saying which — `0` for
/// [`Transferred`](Self::Transferred), `1` for [`Error`](Self::Error)
/// — and the rest is that variant's own bytes.
///
/// # How it ends
///
/// | the channel ends with | means |
/// |-----------------------|-------|
/// | a [`Transferred`](Self::Transferred), then a finish | the file is at the destination |
/// | an [`Error`](Self::Error), then a finish | it is not, and nothing partial is |
///
/// Only a finish ends it. What a caller does about an error is not
/// specified here.
///
/// # The failure is this scope's, the answer is not
///
/// [`Transferred`](Self::Transferred) carries
/// [`transfer::response::Frame`](crate::shared::containers::transfer::response::Frame),
/// which means the same thing wherever the exchange happens. The
/// [`Error`](Self::Error) beside it is the exchange's own.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
/// The file is at the destination. Tag `0`.
Transferred(transfer::response::Frame),
/// A failure. Tag `1`.
///
/// See [`shared::error::Error`](crate::shared::error::Error) for
/// why it says so little.
Error(Error),
}
/// Tag for [`Frame::Transferred`].
const TRANSFERRED: u8 = 0;
/// Tag for [`Frame::Error`].
const ERROR: u8 = 1;
impl Encode for Frame {
/// The ordinary JSON failure, from the half that has one. The
/// other cannot fail.
type Error = serde_json::Error;
// Spelled out rather than `Self::Error`: this enum has a variant
// called `Error`, so the associated type is ambiguous by that name.
fn encode(
&self,
out: &mut Writer<'_>,
) -> Result<(), serde_json::Error> {
match self {
Frame::Transferred(inner) => {
out.extend_from_slice(&[TRANSFERRED]);
// Its error is `Infallible`, and an empty match on one
// is how you say so: there is no value to handle.
inner.encode(out).map_err(|error| match error {})
}
Frame::Error(error) => {
out.extend_from_slice(&[ERROR]);
error.encode(out)
}
}
}
}
impl Decode<'_> for Frame {
/// Three ways to fail, and only one of them is a parse.
type Error = FrameError;
// Spelled out for the same reason as `encode` above.
fn decode(bytes: &[u8]) -> Result<Self, FrameError> {
let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
match *tag {
TRANSFERRED => transfer::response::Frame::decode(rest)
.map(Frame::Transferred)
.map_err(|error| match error {}),
ERROR => {
Error::decode(rest).map(Frame::Error).map_err(FrameError::Error)
}
tag => Err(FrameError::UnknownTag(tag)),
}
}
}
/// A transfer response frame that could not be read.
#[derive(Debug)]
pub enum FrameError {
/// No bytes at all, so not even a tag.
Empty,
/// A tag that is neither of this frame's two.
UnknownTag(u8),
/// The error did not parse.
Error(serde_json::Error),
}
impl fmt::Display for FrameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FrameError::Empty => f.write_str("transfer response frame is empty"),
FrameError::UnknownTag(tag) => {
write!(f, "unknown transfer response frame tag {tag}")
}
FrameError::Error(error) => {
write!(f, "transfer error did not parse: {error}")
}
}
}
}
impl std::error::Error for FrameError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FrameError::Error(error) => Some(error),
FrameError::Empty | FrameError::UnknownTag(_) => None,
}
}
}
and by diverge-sdk-rs/src/shared/containers/transfer/response/frame.rs:
//! The file is at the destination.
use std::convert::Infallible;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// The file landed in the other container.
///
/// It carries nothing, because saying so IS the whole message — no
/// fields, and no bytes on the wire either. The endpoints that allow
/// a transfer to fail wrap this in an enum of their own, as every
/// [`containers`](crate::provider::endpoints::containers) scope does, and that
/// enum's tag does the discriminating.
///
/// # What a partial transfer leaves behind
///
/// Nothing at the destination. The write into the other container is
/// the proxy's own write, and holds what a
/// [`write_path`](crate::shared::containers::write_path::response::Frame)
/// holds: the destination is the old file, then nothing, then the new
/// one — never a prefix of the new one. A read that ends short, or in
/// an error, is a write abandoned, and the destination is as it was.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Frame;
impl Encode for Frame {
/// [`Infallible`]: writing nothing has no failure mode.
type Error = Infallible;
fn encode(&self, _out: &mut Writer<'_>) -> Result<(), Self::Error> {
Ok(())
}
}
impl Decode<'_> for Frame {
/// [`Infallible`]: there is nothing to read.
type Error = Infallible;
/// Whatever bytes arrive are ignored. There are none to send, so a
/// peer that sent some knows something this version does not, and
/// leaving room for it is cheaper than refusing it.
fn decode(_bytes: &[u8]) -> Result<Self, Self::Error> {
Ok(Frame)
}
}