Response
The server sends exactly one response, Written or an error, once the content channel has finished, and the finish.
[0] written: the file is at the path
[1][error JSON …] not written, and nothing partial is
- The sequence. Exactly one response, followed by the response finish. The response is sent only once the content channel has finished, or the write has failed.
- A finish with nothing. A response finish that no response precedes states that the server could not read the request.
The response is defined by diverge-sdk-rs/src/container_proxy/outside/endpoints/filesystem/write/server/response/frame.rs:
//! What a server's response frame carries for a write.
use std::fmt;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
use crate::shared::containers::write_path;
use crate::shared::error::Error;
/// The file landed, or it did not.
///
/// A payload leads with one byte saying which — `0` for
/// [`Written`](Self::Written), `1` for [`Error`](Self::Error) — and
/// the rest is that variant's own bytes.
///
/// # How it ends
///
/// | the scope ends with | means |
/// |---------------------|-------|
/// | a [`Written`](Self::Written), then a finish | the file is at the path |
/// | an [`Error`](Self::Error), then a finish | it is not, and nothing partial is |
///
/// Only a finish ends it, and it comes only once the content channel
/// has: the proxy answers when it knows.
///
/// # The failure is this scope's, the answer is not
///
/// [`Written`](Self::Written) carries
/// [`write_path::response::Frame`](crate::shared::containers::write_path::response::Frame),
/// which means the same thing wherever the exchange happens. The
/// [`Error`](Self::Error) beside it is the exchange's own: a missing
/// parent, a destination that cannot be replaced, content that ended
/// in an error.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
/// The file is at the path. Tag `0`.
Written(write_path::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::Written`].
const WRITTEN: 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::Written(inner) => {
out.extend_from_slice(&[WRITTEN]);
// 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 {
WRITTEN => write_path::response::Frame::decode(rest)
.map(Frame::Written)
.map_err(|error| match error {}),
ERROR => {
Error::decode(rest).map(Frame::Error).map_err(FrameError::Error)
}
tag => Err(FrameError::UnknownTag(tag)),
}
}
}
/// A write 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("write response frame is empty"),
FrameError::UnknownTag(tag) => {
write!(f, "unknown write response frame tag {tag}")
}
FrameError::Error(error) => {
write!(f, "write 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/write_path/response/frame.rs:
//! What a response frame carries on a write path channel.
use std::convert::Infallible;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// The file landed.
///
/// It carries nothing, because saying so IS the whole message — no
/// fields, and no bytes on the wire either.
///
/// # Failing is not this type's business
///
/// It used to spend a tag byte against the day failure got a shape.
/// Failure has one now, and it is not here: the endpoints that allow a
/// write to fail wrap this in an enum of their own — every
/// [`containers`](crate::provider::endpoints::containers) scope does — and that
/// enum's tag does the discriminating. A byte here
/// as well would be two discriminators for one choice.
///
/// What a write can fail with is the exchange's business. What "the
/// file landed" looks like is not, and it looks like nothing.
///
/// # What a partial write leaves behind
///
/// Nothing at the destination. A provider writes to a temporary in the
/// destination's own directory and renames it into place, so the path
/// holds the old file, then nothing, then the new one — never a prefix
/// of the new one. That holds whether this frame arrives or not.
///
/// Where space is too tight for both copies, unlinking the old one
/// first frees exactly what the new one needs. That trades the old
/// contents away on failure, which is why it is worth doing only after
/// the ordinary attempt returns `ENOSPC` rather than up front.
#[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)
}
}