redirectionio 2.5.2

Redirection IO Library to handle matching rule, redirect and filtering headers and body.
use crate::action::Action;
use crate::http::{Header, Request};
use serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Log {
    code: u16,
    to: String,
    time: u64,
    proxy: String,
    ips: Option<Vec<String>>,
    from: FromLog,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct FromLog {
    #[serde(rename = "ruleIds")]
    rule_ids: Option<Vec<String>>,
    url: String,
    method: Option<String>,
    scheme: Option<String>,
    host: Option<String>,
    referer: Option<String>,
    #[serde(rename = "userAgent")]
    user_agent: Option<String>,
    #[serde(rename = "contentType")]
    content_type: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LegacyLog {
    status_code: u16,
    host: Option<String>,
    method: Option<String>,
    request_uri: Option<String>,
    user_agent: Option<String>,
    referer: Option<String>,
    scheme: Option<String>,
    use_json: Option<bool>,
    target: Option<String>,
    rule_id: Option<String>,
}

impl Log {
    pub fn from_legacy(legacy: LegacyLog, proxy: String) -> Self {
        let now = match SystemTime::now().duration_since(UNIX_EPOCH) {
            Err(_) => 0,
            Ok(time) => time.as_millis() as u64,
        };

        Log {
            code: legacy.status_code,
            to: legacy.target.unwrap_or_default(),
            time: now,
            proxy,
            ips: None,
            from: FromLog {
                rule_ids: legacy.rule_id.map(|id| vec![id]),
                url: legacy.request_uri.unwrap_or_default(),
                method: legacy.method,
                scheme: legacy.scheme,
                host: legacy.host,
                referer: legacy.referer,
                user_agent: legacy.user_agent,
                content_type: None,
            },
        }
    }

    pub fn from_proxy(
        request: &Request,
        code: u16,
        response_headers: &[Header],
        action: Option<&Action>,
        proxy: &str,
        time: u64,
        client_ip: &str,
    ) -> Log {
        let mut location = None;
        let mut user_agent = None;
        let mut referer = None;
        let mut content_type = None;
        let mut ips = vec![client_ip.to_string()];

        for header in &request.headers {
            if header.name.to_lowercase() == "user-agent" {
                user_agent = Some(header.value.clone())
            }

            if header.name.to_lowercase() == "referer" {
                referer = Some(header.value.clone())
            }

            if header.name.to_lowercase() == "x-forwarded-for" {
                let forwarded_ips = header.value.split(',');

                for forwarded_ip in forwarded_ips {
                    ips.push(forwarded_ip.trim().to_string());
                }
            }
        }

        for header in response_headers {
            if header.name.to_lowercase() == "location" {
                location = Some(header.value.clone())
            }

            if header.name.to_lowercase() == "content-type" {
                content_type = Some(header.value.clone())
            }
        }

        let from = FromLog {
            rule_ids: action.map(|a| a.get_applied_rule_ids()),
            url: request.path_and_query_skipped.original.clone(),
            method: request.method.clone(),
            scheme: request.scheme.clone(),
            host: request.host.clone(),
            referer,
            user_agent,
            content_type,
        };

        Log {
            code,
            from,
            proxy: proxy.to_string(),
            time,
            ips: Some(ips),
            to: location.unwrap_or_else(|| "".to_string()),
        }
    }
}