Request
The payload of the channel request the client sends: the tag byte 1 and the ask.
[1][request JSON …]
digest. The blob digest,<algorithm>:<hex>.- Malformed. A channel request whose payload does not decode as stated is answered by a channel response finish that no channel response precedes.
The channel request is defined by diverge-sdk-rs/src/provider/endpoints/images/oci/client/channel_request/frame.rs:
//! What a client's channel request frame carries on an image pull.
use std::fmt;
use crate::shared::containers::oci;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// What a caller asks a provider for on an image pull: the three asks
/// of [`oci`](crate::shared::containers::oci), byte for byte as the
/// provider asks them of a caller on a run scope — the same tags, the
/// same request bodies, the same answers — and the disconnect that
/// ends the scope.
///
/// | tag | asks for |
/// |-----|----------|
/// | `0` | [`OciManifest`](Self::OciManifest) |
/// | `1` | [`OciBlob`](Self::OciBlob) |
/// | `2` | [`OciHas`](Self::OciHas) |
/// | `3` | [`Disconnect`](Self::Disconnect) |
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Frame {
/// A manifest of an image the provider holds for the caller, by
/// digest. Tag `0`.
///
/// Answered with at most one frame, the media type and the bytes,
/// then the finish; the empty finish for a digest the provider
/// does not hold for the caller. See
/// [`oci::manifest`](crate::shared::containers::oci::manifest).
OciManifest(oci::manifest::request::Request),
/// A blob of such an image, by digest. Tag `1`.
///
/// Answered with the blob's bytes as zero or more frames, each
/// appending, then the finish; zero frames is a digest the
/// provider does not hold for the caller. See
/// [`oci::blob`](crate::shared::containers::oci::blob).
OciBlob(oci::blob::request::Request),
/// Whether the provider holds an image for the caller, by digest,
/// and under what name. Tag `2`.
///
/// Answered with one frame, the byte `0` or the byte `1` and a
/// repository path, then the finish. See
/// [`oci::has`](crate::shared::containers::oci::has).
OciHas(oci::has::request::Request),
/// Leave the scope. Tag `3`.
///
/// Carries nothing — the variant is bare — and nothing comes back
/// on this channel: the provider sends no channel response and no
/// channel response finish on it. What comes back is the scope's
/// finish, after every ask still being answered has been.
Disconnect,
}
/// Tag for [`Frame::OciManifest`].
const OCI_MANIFEST: u8 = 0;
/// Tag for [`Frame::OciBlob`].
const OCI_BLOB: u8 = 1;
/// Tag for [`Frame::OciHas`].
const OCI_HAS: u8 = 2;
/// Tag for [`Frame::Disconnect`].
const DISCONNECT: u8 = 3;
/// A tag, then the ask's own JSON; the disconnect is the tag alone.
impl Encode for Frame {
/// The ordinary JSON failure, from the three variants that have
/// one. A lone tag byte cannot fail.
type Error = serde_json::Error;
fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
match self {
Frame::OciManifest(request) => {
out.extend_from_slice(&[OCI_MANIFEST]);
request.encode(out)
}
Frame::OciBlob(request) => {
out.extend_from_slice(&[OCI_BLOB]);
request.encode(out)
}
Frame::OciHas(request) => {
out.extend_from_slice(&[OCI_HAS]);
request.encode(out)
}
Frame::Disconnect => {
out.extend_from_slice(&[DISCONNECT]);
Ok(())
}
}
}
}
impl Decode<'_> for Frame {
/// Three ways to fail: nothing, a tag that is none of the four, or
/// an ask that did not parse.
type Error = FrameError;
fn decode(bytes: &[u8]) -> Result<Self, Self::Error> {
let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
match *tag {
OCI_MANIFEST => oci::manifest::request::Request::decode(rest).map(Frame::OciManifest).map_err(FrameError::Oci),
OCI_BLOB => oci::blob::request::Request::decode(rest).map(Frame::OciBlob).map_err(FrameError::Oci),
OCI_HAS => oci::has::request::Request::decode(rest).map(Frame::OciHas).map_err(FrameError::Oci),
DISCONNECT => Ok(Frame::Disconnect),
tag => Err(FrameError::UnknownTag(tag)),
}
}
}
/// An image pull channel request 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 four.
UnknownTag(u8),
/// An ask did not parse.
Oci(serde_json::Error),
}
impl fmt::Display for FrameError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FrameError::Empty => f.write_str("image pull channel request frame is empty"),
FrameError::UnknownTag(tag) => write!(f, "unknown image pull channel request tag {tag}"),
FrameError::Oci(error) => write!(f, "image pull ask did not parse: {error}"),
}
}
}
impl std::error::Error for FrameError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FrameError::Oci(error) => Some(error),
FrameError::Empty | FrameError::UnknownTag(_) => None,
}
}
}
and by diverge-sdk-rs/src/shared/containers/oci/blob/request/request.rs:
//! Asking for a blob by its digest.
use serde::{Deserialize, Serialize};
use serde_json::Error;
use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
/// The digest, and deliberately nothing else.
///
/// No offset and no length: the provider asks for the blob, and
/// what it does with the bytes — holds them, streams them through —
/// is its own, so long as what it serves is the digest's. No name,
/// because a blob is identified by its digest and a blob under one
/// name is the same blob under another.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Request {
/// The blob's digest, `<algorithm>:<hex>`.
pub digest: String,
}
/// Its JSON, and nothing in front of it. The tag that says which
/// request this is belongs to whichever frame carries it.
impl Encode for Request {
/// The ordinary JSON failure.
type Error = Error;
fn encode(&self, out: &mut Writer<'_>) -> Result<(), Error> {
serde_json::to_writer(out, self)
}
}
impl Decode<'_> for Request {
/// The ordinary JSON failure.
type Error = Error;
fn decode(bytes: &[u8]) -> Result<Self, Error> {
serde_json::from_slice(bytes)
}
}