rambl_rs 0.1.10

An HTTP server framework
Documentation
use std::{borrow::Cow, collections::HashMap, fmt::Display, rc::Rc, sync::Arc, time::{SystemTime, UNIX_EPOCH}};

/// If a type has implemented this, it can be thrown down the responder respond
/// method, without having to do any modifications.
pub trait Response: Sync + Send {
    fn as_response(&self) -> Vec<u8>;
}

/// Creates a valid http response when being responded with.
pub struct HttpResponse<'a> {
    headers: Vec<(&'a str, &'a str)>,
    body: Cow<'a, str>,
}

/// Binary protocol response, can be used for efficient communication
/// Uses the first 8 bytes to encode length as le bytes.
pub struct BinaryResponse {
    payload: Vec<u8>,
}
/// Great for SSE's, auto appends required headers.
/// However, for custom headers, an HttpResponse is required.
pub struct SSEResponse<'a> {
    message: Cow<'a, str>,
    fields: HashMap<Cow<'a, str>, Cow<'a, str>>
}

/// Raw response, no hand holding. Not recommended
pub struct RawResponse<'a> {
    payload: Cow<'a, [u8]>,
}

impl<'a> HttpResponse<'a> {
    /// Creates a new HttpResponse;
    pub fn new() -> Self {
        HttpResponse { headers: Vec::new(), body: Cow::Borrowed("") }
    }

    /// Sets a new header
    pub fn header(&mut self, name: &'a str, value: &'a str) {
        self.headers.push((name, value));
    }

    /// Sets the body
    pub fn body<T>(&mut self, body: T) 
    where
        T: Into<Cow<'a, str>>
    {
        self.body = body.into();
    }
}

impl<'a> SSEResponse<'a> {
    /// Creates a new HttpResponse;
    pub fn new() -> Self {
        SSEResponse { message: Cow::Borrowed(""), fields: HashMap::new() }
    }

    /// Sets the message
    pub fn message<M>(&mut self, message: M) 
    where
        M: Into<Cow<'a, str>>
    {
        self.message = message.into();
    }

    /// Adds a field to the final data.
    /// You can imagine the final data as: "data: {"message": self.message, "timestamp": Instant::now(), "custom_field": custom_value}"
    /// The field name is auto wrapped in double quotes, not the value.
    pub fn field<T, U>(&mut self, field_name: T, field_value: U) 
    where
        T: Into<Cow<'a, str>>,
        U: Into<Cow<'a, str>>,
    {
        self.fields.insert(field_name.into(), field_value.into());
    }

    /// Contains initial response for establishing eventstream connection
    pub fn init() -> Vec<u8> {
        "HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\nCache-Control: no-cache\r\nConnection: keep-alive\r\n\r\n".into()
    }
}

impl BinaryResponse {
    /// Creates a new BinaryResponse.
    /// When this type is responded with, it sends the length as u64 in the beginning, using the method to_le_bytes.
    pub fn new(payload: Vec<u8>) -> Self {
        BinaryResponse { payload }
    }
}

impl<'a> RawResponse<'a> {
    /// Creates a new RawResponse.
    /// When this type is responded with, it sends the raw payload
    pub fn new<T>(payload: T) -> Self
    where 
        T: Into<Cow<'a, [u8]>>
    {
        RawResponse { payload: payload.into() }
    }
}

fn is_valid(str: &str) -> bool {
    !(str.contains("\r") || str.contains("\n") || str.contains("\0"))
}

fn respond_http<T: Display>(payload: &T, len: usize, headers: Option<&Vec<(&str, &str)>>) -> String {
    let mut headers_str = String::new();
    if let Some(headers) = headers {
        headers_str = headers.iter().map(|e| {
            if is_valid(e.0) && is_valid(e.1) && e.0 != "Content-Length" {format!("{}: {}\r\n", e.0, e.1)} else {"".to_string()}
        }).collect::<String>()
    }
    format!("HTTP/1.1 {}\r\nContent-Length: {}\r\n{}\r\n{}", 
        if len == 0 {"204 NO CONTENT"} else {"200 OK"},
        if len != 0 {len} else {0},
        headers_str,
        payload
    )
}

// POINTERS

impl<'a> Response for Box<dyn Response>
{
    fn as_response(&self) -> Vec<u8> {
        (**self).as_response()
    }
}

impl<'a, T> Response for &T
where 
    T: Response
{
    fn as_response(&self) -> Vec<u8> {
        (**self).as_response()
    }
}

// CUSTOM

impl<'a> Response for SSEResponse<'a> {
    fn as_response(&self) -> Vec<u8> {
        format!("data: {}\"message\": \"{}\", \"timestamp\": \"{}\"{}{}\r\n\r\n",
            "{",
            self.message,
            SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis(),
            self.fields.iter().map(|e| {format!(r#","{}": {}"#, e.0, e.1)}).collect::<String>(),
            "}"
        ).into_bytes()
    }
}

impl<'a> Response for HttpResponse<'a> {
    fn as_response(&self) -> Vec<u8> {
        respond_http(&self.body, self.body.len(), Some(&self.headers)).into_bytes()
    }
}

impl<'a> Response for RawResponse<'a> {
    fn as_response(&self) -> Vec<u8> {
        self.payload.as_ref().into()
    }
}

impl Response for BinaryResponse {
    fn as_response(&self) -> Vec<u8> {
        let mut payload = Vec::new();

        payload.extend((self.payload.len() as u64).to_le_bytes());
        payload.extend(&self.payload[..]);

        payload
    }
}

// EMPTY

impl Response for () {
    fn as_response(&self) -> Vec<u8> {
        respond_http(&"", 0, None).into_bytes()
    }
}

// STRINGS

impl Response for &str {
    fn as_response(&self) -> Vec<u8> {
        respond_http(&self, self.len(), None).into_bytes()
    }
}

impl Response for String {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, self.len(), None).into_bytes()
    }
}

impl Response for Vec<u8> {
    fn as_response(&self) -> Vec<u8> {
        self.to_owned()
    }
}

// NUMBERS

impl Response for i8 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 1, None).into_bytes()
    }
}

impl Response for u8 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 1, None).into_bytes()
    }
}

impl Response for i16 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 2, None).into_bytes()
    }
}

impl Response for u16 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 2, None).into_bytes()
    }
}

impl Response for i32 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 4, None).into_bytes()
    }
}

impl Response for u32 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 4, None).into_bytes()
    }
}

impl Response for i64 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 8, None).into_bytes()
    }
}

impl Response for u64 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 8, None).into_bytes()
    }
}

impl Response for i128 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 16, None).into_bytes()
    }
}

impl Response for u128 {
    fn as_response(&self) -> Vec<u8> {
        respond_http(self, 16, None).into_bytes()
    }
}