Response
The server sends exactly one response: the byte 0 alone, Serving, after which the scope stays open until the client stops it or the container ends; or the byte 1 followed by an error as one JSON value, and the response finish.
The server sends exactly one response on the scope, and sends it before it serves any channel.
[0] serving: the proxy serves the directory, and the asks are answered
[1][error JSON …] not served
- The sequence. Exactly one response, first. After a response
whose payload is exactly the one byte
0, the scope stays open: the server sends no other response on the scope, answers every channel the client opens as Client Channels states, and sends the response finish after the last answer when the client stops or the container ends. After a response whose first byte is1, the response finish follows the response, and no frame follows the finish. A response finish that no response precedes states that the request was not served, as Endpoints provides. - Serving. The server sends the byte
0only after the proxy of the container has answeredServingon the filesystem::fuse_serve scope the server opened for the request. - The error. A payload whose first byte is
1carries an error after that byte, running to the end of the payload. The error is exactly one JSON value, in the form defined on the volumes::list response page. An id that names no container the client is running is answered with exactly{"kind":"missing"}; a directory the proxy refused is answered with the error the proxy sent, verbatim. This revision prescribes nothing else about its content. - Malformed. A payload with no byte, a payload whose first byte
is none of
0and1, a payload whose first byte is0and that has a byte after it, and a payload whose bytes after1are not one JSON value are malformed.
The response is defined by diverge-sdk-rs/src/provider/endpoints/containers/fuse_serve/server/response/frame.rs:
//! What a server's response frame carries for a container FUSE serve.
use std::fmt;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
use crate::shared::error::Error;
/// The subtree is served, or the news that it will not be.
///
/// A payload leads with one byte saying which — `0` for
/// [`Serving`](Self::Serving), `1` for [`Error`](Self::Error) — and
/// the rest is that variant's own JSON, of which only the error has
/// any.
///
/// # How the scope runs
///
/// | the scope | means |
/// |-----------|-------|
/// | a serving, then quiet, and stays open | the proxy serves the subtree, and every ask the caller opens is answered on its channel |
/// | a serving, then a finish | the caller stopped, or the container ended |
/// | an error, then a finish | it was not served: no container of the caller's runs under the id, the path is not a directory of it, or the proxy refused |
///
/// Exactly one response, first. Nothing else comes on channel `0`;
/// the answers to asks come on the asks' channels.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
/// The proxy serves the subtree, and asks may be opened. Tag `0`.
Serving,
/// A failure. Tag `1`.
///
/// `{"kind":"missing"}` for an id under which no container of the
/// caller's runs — whether somebody else's does is not said —
/// and otherwise the proxy's own words for the path. See
/// [`shared::error::Error`](crate::shared::error::Error) for why
/// it says so little.
Error(Error),
}
/// Tag for [`Frame::Serving`].
const SERVING: u8 = 0;
/// Tag for [`Frame::Error`].
const ERROR: u8 = 1;
impl Encode for Frame {
/// The ordinary JSON failure, from the half that has one.
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::Serving => {
out.extend_from_slice(&[SERVING]);
Ok(())
}
Frame::Error(error) => {
out.extend_from_slice(&[ERROR]);
error.encode(out)
}
}
}
}
impl Decode<'_> for Frame {
/// Three ways to fail, and 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 {
SERVING => Ok(Frame::Serving),
ERROR => Error::decode(rest).map(Frame::Error).map_err(FrameError::Error),
tag => Err(FrameError::UnknownTag(tag)),
}
}
}
/// A container FUSE serve 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 none 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("container FUSE serve response frame is empty"),
FrameError::UnknownTag(tag) => write!(f, "unknown container FUSE serve response frame tag {tag}"),
FrameError::Error(error) => write!(f, "container FUSE serve 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,
}
}
}