nomhttp 0.1.0

Parser HTTP for the rustyproxy project based on nom
Documentation
use crate::http_header::HttpHeader;
use crate::http_method::HttpMethod;
use crate::http_version::HttpVersion;

#[derive(Debug, Clone)]
pub struct HttpRequest {
    method: HttpMethod,
    path: String,
    version: HttpVersion,
    headers: Vec<HttpHeader>,
    body: Vec<u8>,
    contentlength: Option<usize>,
    destination: Option<String>,
    head: Vec<u8>,
    destination_base: Option<String>,
}

impl std::fmt::Display for HttpRequest {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl HttpRequest {
    pub fn new(
        method: HttpMethod,
        path: String,
        version: HttpVersion,
        headers: Vec<HttpHeader>,
        body: Vec<u8>,
        head: Vec<u8>,
        ssl: bool,
    ) -> Self {
        let contentlength = headers.iter().find(|x| x.name() == "Content-Length");

        let contentlength = match contentlength {
            Some(x) => match x.value().parse::<usize>() {
                Ok(x) => Some(x),
                Err(_) => Some(0),
            },
            None => Some(0),
        };

        let (destination, destination_base) = match headers.iter().find(|h| h.name() == "Host") {
            Some(h) => {
                let v = h.value();
                let destination = match v.contains(':') {
                    true => h.value().to_owned(),
                    false => match ssl {
                        true => {
                            format!("{}:443", h.value())
                        }
                        _ => {
                            format!("{}:80", h.value())
                        }
                    },
                };
                (Some(destination), Some(h.value().to_owned()))
            }
            None => (None, None),
        };

        HttpRequest {
            method,
            path,
            version,
            headers,
            body,
            contentlength,
            destination,
            head,
            destination_base,
        }
    }

    pub fn is_terminated(&self) -> bool {
        if let Some(size) = self.contentlength {
            size == self.body.len()
        } else {
            true
        }
    }

    pub fn method(&self) -> &HttpMethod {
        &self.method
    }

    pub fn path(&self) -> &String {
        &self.path
    }

    pub fn set_path(&mut self, value: &String) {
        self.path = value.to_string();
    }

    pub fn version(&self) -> &HttpVersion {
        &self.version
    }

    pub fn headers(&self) -> &Vec<HttpHeader> {
        &self.headers
    }

    pub fn headers_mut(&mut self) -> &mut Vec<HttpHeader> {
        &mut self.headers
    }

    pub fn body(&self) -> &Vec<u8> {
        &self.body
    }

    pub fn content_length(&self) -> Option<usize> {
        self.contentlength
    }

    pub fn destination(&self) -> Option<&String> {
        if let Some(d) = &self.destination {
            Some(d)
        } else {
            None
        }
    }

    pub fn destination_base(&self) -> Option<&String> {
        if let Some(d) = &self.destination_base {
            Some(d)
        } else {
            None
        }
    }

    pub fn as_bytes(&self) -> Vec<u8> {
        let mut full_content = vec![];

        full_content.extend_from_slice(&mut format!("{} {} {}\r\n", self.method, self.path, self.version).as_bytes());
        for h in &self.headers {
            full_content.append(&mut h.as_bytes());
        }
        full_content.extend_from_slice(b"\r\n"); // append end of request HEAD
        for b in &self.body {
            full_content.push(*b);
        }

        full_content
    }

    pub fn is_header_terminated(&self) -> bool {
        let tail: Vec<u8> = self.head.iter().rev().take(4).copied().collect();

        matches!(tail[..], [0x0a, 0x0d, 0x0a, 0x0d])
    }

    pub fn body_mut(&mut self) -> &mut Vec<u8> {
        &mut self.body
    }

    pub fn keep_alive(&self) -> bool {
        let kah = match self.headers.iter().find(|h| h.name() == "Connection") {
            Some(h) => h.value().to_owned(),
            None => "Close".to_string(),
        };

        !matches!(kah.as_ref(), "Close")
    }

    /*
    pub fn duplicate(&self) -> Self {
        HttpRequest {
            method: self.method,
            path: self.path,
            version: self.version,
            headers: self.headers,
            body: self.body,
            contentlength: self.contentlength,
            destination: self.destination,
            head: self.head,
            destination_base: self.destination_base,
        }
    }
    */
}