use std::{ collections::*, convert::{TryFrom}, }; use serde::{Deserialize, Serialize}; use tokio::sync::mpsc; // Hyper doesn't seem to make it easy to de/ser requests // and responses and stuff like that, so I do it by hand here. pub enum Error { UnsupportedMethod, InvalidHeaderName, } impl From for Error { fn from (_x: hyper::header::InvalidHeaderName) -> Self { Self::InvalidHeaderName } } #[derive (Debug, Deserialize, Serialize)] pub enum Method { Get, Head, } impl TryFrom for Method { type Error = Error; fn try_from (x: hyper::Method) -> Result { match x { hyper::Method::GET => Ok (Self::Get), hyper::Method::HEAD => Ok (Self::Head), _ => Err (Error::UnsupportedMethod), } } } #[derive (Deserialize, Serialize)] pub struct RequestParts { pub method: Method, // Technically URIs are subtle and complex but I don't care pub uri: String, // Technically Hyper has headers in a multi-map // but I don't feel like doing that right now. // Headers are _kinda_ ASCII but they can also be binary. // HTTP is nutty. pub headers: HashMap >, } impl RequestParts { pub fn from_hyper ( method: hyper::Method, uri: String, headers: hyper::HeaderMap ) -> Result { use std::iter::FromIterator; let method = Method::try_from (method)?; let headers = HashMap::from_iter ( headers.into_iter () .filter_map (|(k, v)| k.map (|k| (k, v))) .map (|(k, v)| (String::from (k.as_str ()), v.as_bytes ().to_vec ())) ); Ok (Self { method, uri, headers, }) } } #[derive (Deserialize, Serialize)] pub struct WrappedRequest { pub id: String, pub req: RequestParts, } #[derive (Debug, Deserialize, Serialize)] pub enum StatusCode { Ok, // 200 PartialContent, // 206 BadRequest, // 400 Forbidden, // 403 NotFound, // 404 MethodNotAllowed, // 405 } impl Default for StatusCode { fn default () -> Self { Self::Ok } } impl From for hyper::StatusCode { fn from (x: StatusCode) -> Self { match x { StatusCode::Ok => Self::OK, StatusCode::PartialContent => Self::PARTIAL_CONTENT, StatusCode::BadRequest => Self::BAD_REQUEST, StatusCode::Forbidden => Self::FORBIDDEN, StatusCode::NotFound => Self::NOT_FOUND, StatusCode::MethodNotAllowed => Self::METHOD_NOT_ALLOWED, } } } #[derive (Default, Deserialize, Serialize)] pub struct ResponseParts { pub status_code: StatusCode, // Technically Hyper has headers in a multi-map // but I don't feel like doing that right now. // We promise not to need this feature. pub headers: HashMap >, } // reqwest and hyper have different Body types for _some reason_ // so I have to do everything myself type Body = mpsc::Receiver , std::convert::Infallible>>; #[derive (Default)] pub struct Response { pub parts: ResponseParts, pub body: Option , // Needed to make Nginx happy pub content_length: Option , } impl Response { pub fn status_code (&mut self, c: StatusCode) -> &mut Self { self.parts.status_code = c; self } pub fn header (&mut self, k: String, v: Vec ) -> &mut Self { self.parts.headers.insert (k, v); self } pub fn body (&mut self, b: Body) -> &mut Self { self.body = Some (b); self } pub fn body_bytes (&mut self, b: Vec ) -> &mut Self { let (mut tx, rx) = tokio::sync::mpsc::channel (1); tokio::spawn (async move { tx.send (Ok (b)).await.unwrap (); }); self.body = Some (rx); self } }