Response

The server sends no response on the scope, unless it keeps no client image, in which case it sends exactly one, the byte 0 alone, and the response finish; otherwise the response finish follows the last answer when the client disconnects or its connection ends, and a response finish the client did not ask for states that the scope is not served.

A server that holds out the images_store capability sends no response on the scope: every answer comes on the channel of the ask it answers. A server that does not sends exactly one response, the byte 0 alone, and the response finish.

(nothing)      the main stream carries no response: the pull is served
[0]            once, alone: the server keeps no client image, and serves no channel
  • The sequence. On a server that keeps client images, no response: the response finish follows the last answer of every channel still open when the server receives the client’s disconnect or the client’s connection ends, and no frame follows the finish. On a server that keeps none, exactly one response, the byte 0 and nothing else, then the response finish; the server answers no channel, before the response or after it.
  • Unsupported. A payload that is the byte 0 and nothing else states that the server holds out no images_store capability and holds no image for the client, as Capabilities provides. Bytes after it are not read.
  • Not served. A response finish that the client did not ask for by a disconnect, and that no response precedes, states that the server will not serve the pull, as Endpoints provides for a request not served. The server serves no channel after it.
  • Malformed. A payload with no byte, and a payload whose first byte is not 0, are malformed; a client ignores them.

The response is defined by diverge-sdk-rs/src/provider/endpoints/images/oci/server/response/frame.rs:

//! What a server's response frame carries on a pull's main stream.

use std::fmt;

use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};

/// The one thing a provider says on channel `0` of a pull, and only
/// when it will not serve one: it has no image store.
///
/// An ordinary pull sends nothing on channel `0` — every answer comes
/// on an ask's own channel, and the finish is the scope's end. A
/// provider whose `images_store` is `false` — see
/// [`info`](crate::provider::endpoints::info) — sends exactly one of
/// these first, and the finish follows it; no ask is answered. A
/// payload is one byte, `0` for [`Unsupported`](Self::Unsupported),
/// and nothing after it.
///
/// | the scope | means |
/// |-----------|-------|
/// | nothing, and stays open | the provider serves the pull, and every ask the caller opens is answered on its channel |
/// | nothing, then a finish | the caller disconnected, or the provider would not serve the pull for a reason it does not say |
/// | an unsupported, then a finish | the provider has no image store; nothing was asked and nothing is held |
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Frame {
    /// The provider has no image store, and will not serve the pull.
    /// Tag `0`.
    ///
    /// Carries nothing. A caller asks another provider; nothing about
    /// the request is the cause.
    Unsupported,
}

/// Tag for [`Frame::Unsupported`].
const UNSUPPORTED: u8 = 0;

/// The tag, and nothing after it.
impl Encode for Frame {
    /// [`Infallible`](std::convert::Infallible): a known byte.
    type Error = std::convert::Infallible;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
        match self {
            Frame::Unsupported => {
                out.extend_from_slice(&[UNSUPPORTED]);
                Ok(())
            }
        }
    }
}

impl Decode<'_> for Frame {
    /// Two ways to fail, and neither of them is a parse.
    type Error = FrameError;

    fn decode(bytes: &[u8]) -> Result<Self, FrameError> {
        let (tag, _) = bytes.split_first().ok_or(FrameError::Empty)?;
        match *tag {
            UNSUPPORTED => Ok(Frame::Unsupported),
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}

/// A pull response frame that could not be read.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FrameError {
    /// No bytes at all, so not even a tag.
    Empty,
    /// A tag that is not this frame's one.
    UnknownTag(u8),
}

impl fmt::Display for FrameError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FrameError::Empty => f.write_str("image pull response frame is empty"),
            FrameError::UnknownTag(tag) => write!(f, "unknown image pull response frame tag {tag}"),
        }
    }
}

impl std::error::Error for FrameError {}