# /filesystem/read

> One file out of the container: the server sends exactly one message naming the file; the proxy sends the bytes of the file in one or more messages and closes the connection, or sends an error as its last message.

Canonical: https://provider.diverge.network/2.3.0/proxy/filesystem/read/
Specification revision: 2.3.0

The server opens `/filesystem/read` and sends exactly one message, the
request defined below as JSON, naming the file. The proxy sends the
bytes of the file and closes the connection.

```text
server → proxy:   [request JSON]                        once
proxy → server:   [0][bytes …] … [0][bytes …]           the file
                  [1][message …]                        last, when the read failed
```

| The path ends with | Meaning |
|--------------------|---------|
| one or more bodies and the close | the bytes of the bodies, concatenated, are the file |
| an error and the close | the file was not read, or was not read whole |
| the close and no message before it | the read was not served |

- **Pieces.** Each body carries at most `2097152` bytes (2 MiB). The
  proxy sends a file of zero bytes as exactly one empty body before
  the close.
- **A file, never a directory.** A path that names a directory is
  answered with an error.
- **No repetition.** An abrupt end of the path is a read that
  failed. No party retries it.

The request is defined by
`diverge-provider-sdk/src/shared/containers/read/request/request.rs`:

```rust
//! One file to read.

use serde::{Deserialize, Serialize};

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

/// Read one file out of the container.
///
/// No offset and no length. A read starts at the beginning and runs to
/// whatever end it finds, because a file being written to has no
/// stable size to address into — see
/// [`response::Frame`](super::super::response::Frame) for what that
/// costs and how it is reported.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub struct Request {
    /// The file, as path components from the container's root.
    ///
    /// The same meaning of "path" as everywhere else in this API, and
    /// the same frame of reference a
    /// [`filetree`](crate::shared::filetree) stream uses — so a caller
    /// watching a container reads a file by handing back the path the
    /// watch just named.
    ///
    /// Components rather than a joined string: a path is a sequence,
    /// and joining it would invent a separator that then has to be
    /// escaped out of names containing it.
    pub path: Vec<String>,
}

impl Encode for Request {
    /// The ordinary JSON failure.
    type Error = serde_json::Error;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
        serde_json::to_writer(out, self)
    }
}

impl Decode<'_> for Request {
    /// The ordinary JSON failure.
    type Error = serde_json::Error;

    fn decode(bytes: &[u8]) -> Result<Self, Self::Error> {
        serde_json::from_slice(bytes)
    }
}
```

The messages are defined by
`diverge-provider-sdk/src/container_proxy/filesystem/read/response/frame.rs`:

```rust
//! A piece of the file, or why there will not be one.

use std::convert::Infallible;

use super::FrameError;
use crate::encode::{Encode, Writer};
use crate::shared::containers::read;

/// One message on `/filesystem/read`.
///
/// ```text
/// [kind: u8][bytes… | message…]
/// ```
///
/// A body is the shared read's own piece —
/// [`read::response::Frame`](crate::shared::containers::read::response::Frame),
/// bytes and nothing else — behind a kind byte, so an error can sit
/// beside it. The byte per chunk is the price of a read that can say
/// why it stopped, and it is small next to the chunk.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Frame<'a> {
    /// Kind `0`. A piece of the file, in order. Empty is a piece: an
    /// empty file is one of these.
    Body(read::response::Frame<'a>),
    /// Kind `1`. The file was not read, or not all of it, and this
    /// says why, for a reader rather than a program: no such file, a
    /// directory, a read that failed partway. The last message before
    /// the close. Nothing here is enumerated, because what a
    /// filesystem refuses is its own business.
    Error(&'a str),
}

impl Encode for Frame<'_> {
    /// [`Infallible`]: a kind byte and bytes copied.
    type Error = Infallible;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Infallible> {
        match self {
            Frame::Body(body) => {
                out.extend_from_slice(&[0]);
                body.encode(out)
            }
            Frame::Error(message) => {
                out.extend_from_slice(&[1]);
                out.extend_from_slice(message.as_bytes());
                Ok(())
            }
        }
    }
}

impl<'a> Frame<'a> {
    /// Decode one message. The body or the message borrows from
    /// `bytes`.
    pub fn decode(bytes: &'a [u8]) -> Result<Self, FrameError> {
        let (kind, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
        match *kind {
            0 => Ok(Frame::Body(read::response::Frame(rest))),
            1 => std::str::from_utf8(rest)
                .map(Frame::Error)
                .map_err(|_| FrameError::MessageUtf8),
            other => Err(FrameError::UnknownKind(other)),
        }
    }
}
```

The ways in which a message fails to decode are defined by
`diverge-provider-sdk/src/container_proxy/filesystem/read/response/error.rs`:

```rust
//! Why a message on `/filesystem/read` could not be decoded.

use std::error;
use std::fmt;

/// A read answer that could not be read.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FrameError {
    /// No bytes at all, so not even a kind.
    ///
    /// Distinct from an empty body, which is a kind byte followed by
    /// nothing and is an empty file's one message.
    Empty,
    /// A kind this answer does not define.
    UnknownKind(u8),
    /// An error message that is not UTF-8.
    MessageUtf8,
}

impl fmt::Display for FrameError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FrameError::Empty => f.write_str("read answer is empty"),
            FrameError::UnknownKind(kind) => {
                write!(f, "unknown read answer kind {kind}")
            }
            FrameError::MessageUtf8 => {
                f.write_str("read error message is not utf-8")
            }
        }
    }
}

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