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}