Response
The server answers with exactly one channel response, the byte 0, or the byte 1 followed by the repository path it holds the image under, and the channel response finish; a finish with nothing before it states that it does not hold the image for the client.
[1][name: utf8 …] once: the server holds the image for the client, under that repository path
[0] once: it does not
- The sequence. Exactly one channel response, followed by the channel response finish. A channel response finish that no channel response precedes states that the server does not hold the image for the client.
- The frame. The byte
0and nothing after it: the server does not hold the image for the client. The byte1followed by one or more bytes of UTF-8: the server holds the image the digest names for the client, and the bytes are the repository path it holds it under, with no host and no tag. A frame whose first byte is neither, a frame of the byte0followed by anything, a frame of the byte1followed by nothing, and a frame whose bytes after1are not UTF-8 are malformed. - What follows. A client that was answered
1may ask for the manifest and blobs of the image on oci-manifest and oci-blob channels.
The channel response is defined by diverge-sdk-rs/src/shared/containers/oci/has/response/frame.rs:
//! The byte `0`, or the byte `1` and a name.
use std::convert::Infallible;
use super::FrameError;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// Whether the caller holds the image, and under what repository
/// path: the byte `0` and nothing after it when it does not; the byte
/// `1` followed by the path as UTF-8 when it does.
///
/// The path is the one the caller holds the image under, which the
/// provider's registry serves it from: a runtime pulls
/// `<registry>/<repository>/<name>@<digest>`, and `name` is this. The
/// caller's store is keyed by digest, so the name is the caller's to
/// answer and not the provider's to ask. A provider refuses a path
/// that is not a repository path, since it lands in a reference by
/// concatenation. A name that is empty is not a name: it encodes as
/// not held.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Frame {
/// The repository path the image is held under, or `None` for not
/// held.
pub name: Option<String>,
}
impl Encode for Frame {
/// A byte and bytes: nothing to fail.
type Error = Infallible;
fn encode(&self, out: &mut Writer<'_>) -> Result<(), Infallible> {
match self.name.as_deref() {
Some(name) if !name.is_empty() => {
out.extend_from_slice(&[1]);
out.extend_from_slice(name.as_bytes());
}
_ => out.extend_from_slice(&[0]),
}
Ok(())
}
}
impl Decode<'_> for Frame {
/// No byte, a first byte that is neither, a `0` with bytes after
/// it, or a `1` with none after it or with bytes that are not
/// UTF-8.
type Error = FrameError;
fn decode(bytes: &[u8]) -> Result<Self, FrameError> {
match bytes {
[] => Err(FrameError::Empty),
[0] => Ok(Frame { name: None }),
[0, ..] => Err(FrameError::Trailing),
[1] => Err(FrameError::Name),
[1, name @ ..] => std::str::from_utf8(name)
.map(|name| Frame {
name: Some(name.to_string()),
})
.map_err(|_| FrameError::Name),
[byte, ..] => Err(FrameError::Byte(*byte)),
}
}
}