http.rsannotatedhttp.rssource94 lines · 2.7 KB · raw
1//! The service's request and response as plain values: no HTTP library's types, so the same layer
2//! serves a native server, a Worker's `fetch` handler and a test.
3
4use ::log::error;
5use serde::Serialize;
6use whiskers_ports::Diagnostic;
7
8/// The largest request body served. A picture arrives as base64 inside the body: a few at 1024
9/// pixels is a few hundred kilobytes. The adapter reads at most `MAX_BODY + 1` bytes; a body that long
10/// is refused.
11pub const MAX_BODY: usize = 8 * 1024 * 1024;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub enum Method {
15    Post,
16    /// Any other method, by name (only ever answered with 405).
17    Other(String),
18}
19
20/// What the adapter managed to read of the request body.
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum Body {
23    Text(String),
24    /// The body could not be read (a device that dropped off wifi halfway, or not text).
25    Unreadable(Diagnostic),
26}
27
28#[derive(Clone, Debug, PartialEq, Eq)]
29pub struct Request {
30    pub method: Method,
31    pub path: String,
32    pub body: Body,
33}
34
35impl Request {
36    pub fn post(path: &str, body: impl Into<String>) -> Self {
37        Self { method: Method::Post, path: path.to_owned(), body: Body::Text(body.into()) }
38    }
39}
40
41/// What a response carries, which the adapter turns into its server's header.
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub enum Content {
44    /// No body and no content type.
45    None,
46    /// `application/json`.
47    Json,
48    /// Plain text, UTF-8 (`text/plain; charset=UTF-8`).
49    Text,
50    /// `audio/mpeg`.
51    Mpeg,
52}
53
54#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct Response {
56    pub status: u16,
57    pub content: Content,
58    pub body: Vec<u8>,
59}
60
61impl Response {
62    pub fn empty(status: u16) -> Self {
63        Self { status, content: Content::None, body: Vec::new() }
64    }
65
66    pub fn json(status: u16, body: String) -> Self {
67        Self { status, content: Content::Json, body: body.into_bytes() }
68    }
69
70    pub fn text(status: u16, body: String) -> Self {
71        Self { status, content: Content::Text, body: body.into_bytes() }
72    }
73
74    pub fn bytes(status: u16, content: Content, body: Vec<u8>) -> Self {
75        Self { status, content, body }
76    }
77
78    /// `value` as a 200 JSON reply. (A reply type of ours that cannot be serialised is a bug, answered
79    /// as a 500 and logged, never a panic in a request.)
80    pub fn of<T: Serialize>(value: &T) -> Self {
81        match serde_json::to_string(value) {
82            Ok(json) => Self::json(200, json),
83            Err(e) => {
84                error!("a reply does not serialise: {e}");
85                Self::empty(500)
86            }
87        }
88    }
89
90    /// The body as text, for tests and logs that know it is.
91    pub fn text_body(&self) -> String {
92        String::from_utf8_lossy(&self.body).into_owned()
93    }
94}