proxide 0.2.3

Proxide is a debugging proxy for capturing HTTP/2 and gRPC traffic.
use bytes::BytesMut;
use chrono::prelude::*;
use http::{HeaderMap, Method, Uri};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::SocketAddr;
use uuid::Uuid;

pub mod events;
pub mod serialization;

#[derive(Serialize, Deserialize, Default)]
pub struct Session
{
    pub connections: IndexedVec<ConnectionData>,
    pub requests: IndexedVec<EncodedRequest>,
}

#[derive(Serialize, Deserialize)]
pub struct IndexedVec<T>
{
    pub items: Vec<T>,
    pub items_by_uuid: HashMap<Uuid, usize>,
}

#[derive(Serialize, Deserialize, Debug)]
pub enum Protocol
{
    Connect,
    Tls,
    Http2,
}

#[derive(Serialize, Deserialize)]
pub struct ConnectionData
{
    pub uuid: Uuid,
    pub client_addr: SocketAddr,
    pub protocol_stack: Vec<Protocol>,
    pub start_timestamp: DateTime<Local>,
    pub end_timestamp: Option<DateTime<Local>>,
    pub status: Status,
}

#[derive(Serialize, Deserialize)]
pub struct RequestData
{
    pub uuid: Uuid,
    pub connection_uuid: Uuid,

    #[serde(with = "http_serde::method")]
    pub method: Method,

    #[serde(with = "http_serde::uri")]
    pub uri: Uri,

    pub start_timestamp: DateTime<Local>,
    pub end_timestamp: Option<DateTime<Local>>,
    pub status: Status,
}

#[derive(Serialize, Deserialize)]
pub struct EncodedRequest
{
    pub request_data: RequestData,
    pub request_msg: MessageData,
    pub response_msg: MessageData,
}

#[derive(Serialize, Deserialize)]
pub struct MessageData
{
    #[serde(with = "http_serde::header_map")]
    pub headers: HeaderMap,

    #[serde(with = "http_serde::header_map")]
    pub trailers: HeaderMap,

    #[serde(with = "serde_base64")]
    pub content: BytesMut,

    pub start_timestamp: Option<DateTime<Local>>,
    pub end_timestamp: Option<DateTime<Local>>,
    pub part: RequestPart,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
pub enum Status
{
    InProgress,
    Succeeded,
    Failed,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
pub enum RequestPart
{
    Request,
    Response,
}

impl MessageData
{
    pub fn new(part: RequestPart) -> Self
    {
        Self {
            headers: Default::default(),
            trailers: Default::default(),
            content: Default::default(),
            start_timestamp: None,
            end_timestamp: None,
            part,
        }
    }

    pub fn with_headers(mut self, h: HeaderMap) -> Self
    {
        self.headers = h;
        self
    }

    pub fn with_start_timestamp(mut self, ts: DateTime<Local>) -> Self
    {
        self.start_timestamp = Some(ts);
        self
    }
}

impl<T> IndexedVec<T>
{
    pub fn push(&mut self, uuid: Uuid, item: T)
    {
        self.items_by_uuid.insert(uuid, self.items.len());
        self.items.push(item);
    }

    pub fn get_index_by_uuid(&self, uuid: Uuid) -> Option<usize>
    {
        self.items_by_uuid.get(&uuid).copied()
    }

    pub fn get_by_uuid(&self, uuid: Uuid) -> Option<&T>
    {
        let idx = self.items_by_uuid.get(&uuid)?;
        self.items.get(*idx)
    }

    pub fn get_mut_by_uuid(&mut self, uuid: Uuid) -> Option<&mut T>
    {
        let idx = self.items_by_uuid.get(&uuid)?;
        self.items.get_mut(*idx)
    }
}

impl<T> std::ops::Deref for IndexedVec<T>
{
    type Target = [T];
    fn deref(&self) -> &Self::Target
    {
        &self.items
    }
}

impl<T> Default for IndexedVec<T>
{
    fn default() -> Self
    {
        Self {
            items: Default::default(),
            items_by_uuid: Default::default(),
        }
    }
}

pub trait HasKey
{
    fn key(&self) -> Uuid;
}

impl HasKey for EncodedRequest
{
    fn key(&self) -> Uuid
    {
        self.request_data.uuid
    }
}

impl std::fmt::Display for Protocol
{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
    {
        write!(
            f,
            "{}",
            match self {
                Protocol::Connect => "CONNECT",
                Protocol::Tls => "TLS",
                Protocol::Http2 => "HTTP/2",
            },
        )
    }
}

mod serde_base64
{
    use bytes::BytesMut;
    pub fn serialize<S: serde::Serializer>(data: &BytesMut, s: S) -> Result<S::Ok, S::Error>
    {
        use serde::Serialize;

        if s.is_human_readable() {
            s.serialize_str(&base64::encode(&data))
        } else {
            data.serialize(s)
        }
    }

    pub fn deserialize<'de, D: serde::Deserializer<'de>>(d: D) -> Result<BytesMut, D::Error>
    {
        use serde::Deserialize;

        if d.is_human_readable() {
            String::deserialize(d)
                .and_then(|s| {
                    base64::decode(&s).map_err(|err| serde::de::Error::custom(err.to_string()))
                })
                .map(|b| BytesMut::from(b.as_slice()))
        } else {
            BytesMut::deserialize(d)
        }
    }
}