Repository: emanuele-em/proxelar (language: rust)

Issue:
Usability Upgrade - Communication between GUI and API with type consistency
Due to me and my initial laziness all communications between API and eGUI are done trough String in the following way:
_1._ Api `Structs` types **->** _2._ Convert all fields to `String` (even in twisted ways) **->** _3._ `Output Struct` (of `String`) **->** _4._ Send `Output Struct` via Thread channel to eGUI **->** _5_. eGui Receives `Output Struct` **->** _6._ eGui convert it to a new `Requests struct` (of `String`) in order to print it correctly.

_**It Sucks.**_

The goal is to avoid string conversion and if possible make order and consistency between API types and eGUI types in this way:
_1._ API Structs types **->** _2._ send that type via Channel **->** _3._ eGui Receives the structs and print it directly




Discussion / hints:
I'll take on this one! I'm thinking the best path here is probably to embed the http response and requests largely as-is:
<img width="1176" alt="Screenshot 2023-02-21 at 12 59 09 PM" src="https://user-images.githubusercontent.com/45983298/220451507-e130339b-f595-4e91-b473-5b1dadbc2d59.png">
<img width="1161" alt="Screenshot 2023-02-21 at 1 00 32 PM" src="https://user-images.githubusercontent.com/45983298/220451504-e3fd5b2b-ebad-43bf-9b8f-634e8f9f3358.png">

The `body` field here in the http library can just be converted to some digestible form, and then stored with the `Parts` for both response and request, providing all of the data we need

Fix patch:
diff --git a/.gitignore b/.gitignore
index 0592392..212de44 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
 /target
-.DS_Store
+.DS_Store
\ No newline at end of file
diff --git a/mitm_proxy/src/mitm_proxy.rs b/mitm_proxy/src/mitm_proxy.rs
index 521ee85..6a6e9fe 100644
--- a/mitm_proxy/src/mitm_proxy.rs
+++ b/mitm_proxy/src/mitm_proxy.rs
@@ -93,11 +93,11 @@ pub struct MitmProxy {
     requests: Vec<RequestInfo>,
     config: MitmProxyConfig,
     state: MitmProxyState,
-    rx: Receiver<Output>,
+    rx: Receiver<ProxyHandler>,
 }
 
 impl MitmProxy {
-    pub fn new(cc: &eframe::CreationContext<'_>, rx: Receiver<Output>) -> Self {
+    pub fn new(cc: &eframe::CreationContext<'_>, rx: Receiver<ProxyHandler>) -> Self {
         Self::configure_fonts(cc);
         let config: MitmProxyConfig = confy::load("MitmProxy", None).unwrap_or_default();
         let state = MitmProxyState::new();
@@ -257,7 +257,10 @@ impl MitmProxy {
 
     pub fn update_requests(&mut self) -> Option<RequestInfo> {
         match self.rx.try_recv() {
-            Ok(l) => Some(RequestInfo::from(l)),
+            Ok(l) => {
+                let (request,response) = l.to_parts();
+                Some(RequestInfo::new(request,response))
+            },
             _ => None,
         }
     }
@@ -295,6 +298,7 @@ impl MitmProxy {
 
                     if clean_btn.clicked() {
                         self.requests = vec![];
+                        self.state.selected_request = None;
                     }
 
                     ui.separator();
diff --git a/mitm_proxy/src/requests.rs b/mitm_proxy/src/requests.rs
index a081192..696b8a1 100644
--- a/mitm_proxy/src/requests.rs
+++ b/mitm_proxy/src/requests.rs
@@ -1,67 +1,7 @@
-use std::collections::HashMap;
-
 use eframe::egui::{self};
 use egui_extras::TableRow;
 use proxyapi::{*, hyper::Method};
 
-struct Request {
-    http_method: Method,    
-    method: String,
-    uri: String,
-    version: String,
-    headers: HashMap<String, String>,
-    body: String,
-    time: i64,
-}
-
-impl Request {
-    fn new(
-        http_method: Method,
-        method: String,
-        uri: String,
-        version: String,
-        headers: HashMap<String, String>,
-        body: String,
-        time: i64,
-    ) -> Self {
-        Self {
-            http_method,
-            method,
-            uri,
-            version,
-            headers,
-            body,
-            time,
-        }
-    }
-}
-
-pub struct Response {
-    status: String,
-    version: String,
-    headers: HashMap<String, String>,
-    body: String,
-    time: i64,
-}
-
-impl Response {
-    fn new(
-        status: String,
-        version: String,
-        headers: HashMap<String, String>,
-        body: String,
-        time: i64,
-    ) -> Self {
-        Self {
-            status,
-            version,
-            headers,
-            body,
-            time,
-        }
-    }
-}
-
 pub struct Details;
 
 #[derive(PartialEq)]
@@ -77,108 +17,65 @@ impl Default for InfoOptions {
     }
 }
 pub struct RequestInfo {
-    request: Option<Request>,
-    response: Option<Response>,
+    request: Option<ProxiedRequest>,
+    response: Option<ProxiedResponse>,
     details: Option<Details>,
 }
-
-impl Default for RequestInfo {
-    fn default() -> Self {
-        RequestInfo {
-            request: None,
-            response: None,
-            details: None,
-        }
-    }
-}
-
-impl From<Output> for RequestInfo {
-    fn from(value: Output) -> Self {
-        let request = match value.req() {
-            Some(r) => Some(Request::new(
-                r.http_method().clone(),
-                r.method().to_string(),
-                r.uri().to_string(),
-                r.version().to_string(),
-                r.headers()
-                    .into_iter()
-                    .map(|h| (h.0.to_string(), h.1.to_string()))
-                    .collect(),
-                r.body().to_string(),
-                r.time(),
-            )),
-            None => None,
-        };
-
-        let response = match value.res() {
-            Some(r) => Some(Response::new(
-                r.status().to_string(),
-                r.version().to_string(),
-                r.headers()
-                    .into_iter()
-                    .map(|h| (h.0.to_string(), h.1.to_string()))
-                    .collect(),
-                r.body().to_string(),
-                r.time(),
-            )),
-            None => None,
-        };
-
-        let details = None;
-
-        RequestInfo {
+impl RequestInfo {
+    pub fn new(request: Option<ProxiedRequest>, response: Option<ProxiedResponse>)->Self{
+        Self {
             request,
             response,
-            details,
+            details:None
         }
     }
 }
-
 impl RequestInfo {
-    pub fn show_request(&mut self, ui: &mut egui::Ui) {
+    pub fn show_request(&self, ui: &mut egui::Ui) {
         if let Some(r) = &self.request {
             ui.strong("Method");
-            ui.label(&r.method);
+            ui.label(r.method().to_string());
 
             ui.strong("Version");
-            ui.label(&r.version);
+            ui.label(format!("{:?}",r.version()));
 
             ui.strong("Headers");
-            for (k, v) in r.headers.iter() {
-                ui.label(format!("{}: {}", &k, &v));
+            for (k, v) in r.headers().iter() {
+                if let Ok(value_str) = v.to_str(){
+                    ui.label(format!("{}: {}", &k, &value_str));
+                }
             }
 
-            ui.strong("body");
-            ui.label(&r.body);
+            ui.strong("Body");
+            ui.label(format!("{:?}",r.body().as_ref()));
 
             ui.strong("Time");
-            ui.label(&r.time.to_string());
+            ui.label(&r.time().to_string());
         } else {
             ui.label("No requests");
         }
     }
 
-    pub fn show_response(&mut self, ui: &mut egui::Ui) {
+    pub fn show_response(&self, ui: &mut egui::Ui) {
         if let Some(r) = &self.response {
             ui.strong("Status");
-            ui.label(&r.status);
+            ui.label(&r.status().to_string());
 
             ui.strong("Version");
-            ui.label(&r.version);
-
-            ui.strong("Status");
-            ui.label(&r.status);
+            ui.label(format!("{:?}",r.version()));
 
             ui.strong("Headers");
-            for (k, v) in r.headers.iter() {
-                ui.label(format!("{}: {}", &k, &v));
+            for (k, v) in r.headers().iter() {
+                if let Ok(value_str) = v.to_str(){
+                    ui.label(format!("{}: {}", &k, &value_str));
+                }
             }
 
-            ui.strong("body");
-            ui.label(&r.body);
+            ui.strong("Body");
+            ui.label(format!("{:?}",r.body().as_ref()));
 
             ui.strong("Time");
-            ui.label(&r.time.to_string());
+            ui.label(&r.time().to_string());
         } else {
             ui.label("No Response");
         }
@@ -186,7 +83,7 @@ impl RequestInfo {
 
     pub fn should_show(&self, method:&Method)->bool {
         if let Some(req) = &self.request {
-            req.http_method == method
+            req.method() == method
         }else{
             false
         }
@@ -202,21 +99,21 @@ impl RequestInfo {
     pub fn render_row(&self, row: &mut TableRow) {
         let req = self.request.as_ref().unwrap();
         let res = self.response.as_ref().unwrap();
-        let time = (res.time as f64 - req.time as f64) * 10_f64.powf(-9.0) as f64;
+        let time = (res.time() as f64 - req.time() as f64) * 10_f64.powf(-9.0) as f64;
         row.col(|ui| {
-            ui.label(&req.uri);
+            ui.label(req.uri().to_string());
         });
 
         row.col(|ui| {
-            ui.label(&req.method);
+            ui.label(req.method().to_string());
         });
 
         row.col(|ui| {
-            ui.label(&res.status);
+            ui.label(res.status().to_string());
         });
 
         row.col(|ui| {
-            ui.label(format!("{} bytes", &res.body.len()));
+            ui.label(format!("{} bytes", res.body().len()));
         });
 
         row.col(|ui| {
diff --git a/proxyapi/src/lib.rs b/proxyapi/src/lib.rs
index 2c8240d..3a47a6c 100644
--- a/proxyapi/src/lib.rs
+++ b/proxyapi/src/lib.rs
@@ -1,7 +1,7 @@
 mod error;
 mod rewind;
 pub mod proxy;
-mod output;
+mod proxy_handler;
 
 pub mod ca;
 
@@ -20,7 +20,7 @@ pub use tokio_tungstenite;
 // pub use error;
 // pub use noop;
 pub use proxy::*;
-pub use output::*;
+pub use proxy_handler::*;
 
 #[derive(Debug)]
 pub enum RequestResponse{
diff --git a/proxyapi/src/proxy/mod.rs b/proxyapi/src/proxy/mod.rs
index 79fa353..03f170f 100644
--- a/proxyapi/src/proxy/mod.rs
+++ b/proxyapi/src/proxy/mod.rs
@@ -5,7 +5,7 @@ use std::{future::Future, net::{SocketAddr}, sync::{Arc, mpsc::SyncSender}, conv
 
 use internal::InternalProxy;
 
-use crate::{ca::{Ssl}, error::Error, output};
+use crate::{ca::{Ssl}, error::Error, proxy_handler};
 
 //use builder::{AddrListenerServer, WantsAddr};
 
@@ -17,12 +17,11 @@ use hyper_rustls::{ HttpsConnectorBuilder};
 
 pub struct Proxy{
     addr: SocketAddr,
-    tx: Option<SyncSender<output::Output>>
+    tx: Option<SyncSender<proxy_handler::ProxyHandler>>
 }
 
 impl Proxy {
-
-    pub fn new(addr: SocketAddr, tx: Option<SyncSender<output::Output>>) -> Self{
+    pub fn new(addr: SocketAddr, tx: Option<SyncSender<proxy_handler::ProxyHandler>>) -> Self{
         Self {
             addr,
             tx
@@ -31,7 +30,7 @@ impl Proxy {
 
     pub async fn start<F: Future<Output = ()>>(self, signal: F) -> Result<(), Error>{
 
-        let addr = self.addr.clone();
+        let addr = self.addr;
 
         let https =  HttpsConnectorBuilder::new()
             .with_webpki_roots()
@@ -53,7 +52,7 @@ impl Proxy {
         let make_service = make_service_fn(move |conn: &AddrStream| {
             let client = client.clone();
             let ca = Arc::clone(&ssl);
-            let http_handler = output::Output::new(self.tx.clone().unwrap());
+            let http_handler = proxy_handler::ProxyHandler::new(self.tx.clone().unwrap());
             let websocket_connector = None;
             let remote_addr = conn.remote_addr();
             async move {
diff --git a/proxyapi/src/output.rs b/proxyapi/src/proxy_handler.rs
similarity index 51%
rename from proxyapi/src/output.rs
rename to proxyapi/src/proxy_handler.rs
index 055874e..50e4d85 100644
--- a/proxyapi/src/output.rs
+++ b/proxyapi/src/proxy_handler.rs
@@ -2,24 +2,29 @@ use std::{collections::HashMap, sync::mpsc::SyncSender};
 
 
 use async_trait::async_trait;
-use http::{HeaderMap, Version, Response, Request, Method};
-use hyper::{Body};
+use bytes::Bytes;
+use http::{HeaderMap, Version, Response, Request, Method, Uri, StatusCode};
+use hyper::{Body, body::to_bytes};
 
 use crate::{HttpHandler, HttpContext, RequestResponse};
 
 #[derive(Clone, Debug)]
-pub struct Output{
-    tx: SyncSender<Output>,
-    req: Option<OutputRequest>,
-    res: Option<OutputResponse>
+pub struct ProxyHandler{
+    tx: SyncSender<ProxyHandler>,
+    req: Option<ProxiedRequest>,
+    res: Option<ProxiedResponse>
 }
 
-impl Output {
-    pub fn new(tx: SyncSender<Output>) -> Self {
+impl ProxyHandler {
+    pub fn new(tx: SyncSender<ProxyHandler>) -> Self {
         Self { tx, req: None, res: None }
     }
 
-    pub fn set_req(&mut self, req: OutputRequest) -> Self{
+    pub fn to_parts(self) -> (Option<ProxiedRequest>, Option<ProxiedResponse>) {
+        (self.req,self.res)
+    }
+
+    pub fn set_req(&mut self, req: ProxiedRequest) -> Self {
         Self { 
             tx: self.clone().tx, 
             req: Some(req),
@@ -27,7 +32,7 @@ impl Output {
         }
     }
 
-    pub fn set_res(&mut self, res: OutputResponse) -> Self{
+    pub fn set_res(&mut self, res: ProxiedResponse) -> Self{
         Self { 
             tx: self.clone().tx, 
             req: self.clone().req,
@@ -41,42 +46,48 @@ impl Output {
         }
     }
 
-    pub fn req(&self) -> &Option<OutputRequest>{
+    pub fn req(&self) -> &Option<ProxiedRequest>{
         &self.req
     }
 
-    pub fn res(&self) -> &Option<OutputResponse>{
+    pub fn res(&self) -> &Option<ProxiedResponse>{
         &self.res
     }
 }
 
+
 #[async_trait]
-impl HttpHandler for Output {
-    async fn handle_request(&mut self, _ctx: &HttpContext, req: Request<Body>, ) -> RequestResponse {
+impl HttpHandler for ProxyHandler {
+    async fn handle_request(&mut self, _ctx: &HttpContext, mut req: Request<Body>, ) -> RequestResponse {
         println!("request{:?}\n", req);
-        let output_request = OutputRequest::new(
+        let mut body_mut = req.body_mut();
+        let body_bytes = to_bytes(&mut body_mut).await.unwrap_or_default();
+        *body_mut = Body::from(body_bytes.clone()); // Replacing the potentially mutated body with a reference to the entire contents
+        
+        let output_request = ProxiedRequest::new(
             req.method().clone(),
-            req.method().to_string(),
-            req.uri().to_string(),
-            req.version().to_string(),
-            req.headers().to_hash_string(),
-            "Body".to_string(),
+            req.uri().clone(),
+            req.version(),
+            req.headers().clone(),
+            body_bytes,
             chrono::Local::now().timestamp_nanos(),
         );
-
         *self = self.set_req(output_request);
         
         req.into()
     }
 
-    async fn handle_response(&mut self, _ctx: &HttpContext, res: Response<Body>) -> Response<Body> {
+    async fn handle_response(&mut self, _ctx: &HttpContext, mut res: Response<Body>) -> Response<Body> {
         println!("res: {:?}\n\n", res);
-        let output_response =  OutputResponse::new(
-            res.status().to_string(),
-            res.version().to_string(),
-            res.headers().to_hash_string(),
-            // Self::get_body(res.body_mut()).await,
-            "Response body".to_string(),
+        let mut body_mut = res.body_mut();
+        let body_bytes = to_bytes(&mut body_mut).await.unwrap_or_default();
+        *body_mut = Body::from(body_bytes.clone()); // Replacing the potentially mutated body with a reference to the entire contents
+        
+        let output_response =  ProxiedResponse::new(
+            res.status(),
+            res.version(),
+            res.headers().clone(),
+            body_bytes,
             chrono::Local::now().timestamp_nanos()
         );
 
@@ -90,29 +101,26 @@ impl HttpHandler for Output {
     
 }
 
-#[derive(Clone, Debug)]
-pub struct OutputRequest {
-    http_method: Method,
-    method: String,
-    uri: String,
-    version: String,
-    headers: HashMap<String, String>,
-    body: String,
+#[derive(Debug,Clone)]
+pub struct ProxiedRequest {
+    method: Method,
+    uri: Uri,
+    version: Version,
+    headers: HeaderMap,
+    body: Bytes,
     time: i64,
 }
 
-impl OutputRequest {
+impl ProxiedRequest {
     fn new(
-        http_method: Method,
-        method: String,
-        uri: String,
-        version: String,
-        headers: HashMap<String, String>,
-        body: String,
+        method: Method,
+        uri: Uri,
+        version: Version,
+        headers: HeaderMap,
+        body: Bytes,
         time: i64,
     ) -> Self {
         Self {
-            http_method,
             method,
             uri,
             version,
@@ -122,27 +130,24 @@ impl OutputRequest {
         }
     }
 
-    pub fn http_method(&self) -> &Method {
-        &self.http_method
-    }
 
-    pub fn method(&self) -> &String {
+    pub fn method(&self) -> &Method {
         &self.method
     }
 
-    pub fn uri(&self) -> &String {
+    pub fn uri(&self) -> &Uri {
         &self.uri
     }
 
-    pub fn version(&self) -> &String {
+    pub fn version(&self) -> &Version {
         &self.version
     }
 
-    pub fn headers(&self) -> &HashMap<String, String> {
+    pub fn headers(&self) -> &HeaderMap {
         &self.headers
     }
 
-    pub fn body(&self) -> &String {
+    pub fn body(&self) -> &Bytes {
         &self.body
     }
 
@@ -151,21 +156,23 @@ impl OutputRequest {
     }
 }
 
-#[derive(Clone, Debug)]
-pub struct OutputResponse {
-    status: String,
-    version: String,
-    headers: HashMap<String, String>,
-    body: String,
+
+#[derive(Debug,Clone)]
+pub struct ProxiedResponse {
+    status: StatusCode,
+    version: Version,
+    headers: HeaderMap,
+    body: Bytes,
     time: i64,
 }
 
-impl OutputResponse {
+
+impl ProxiedResponse {
     fn new(
-        status: String,
-        version: String,
-        headers: HashMap<String, String>,
-        body: String,
+        status: StatusCode,
+        version: Version,
+        headers: HeaderMap,
+        body: Bytes,
         time: i64,
     ) -> Self {
         Self {
@@ -177,19 +184,19 @@ impl OutputResponse {
         }
     }
 
-    pub fn status(&self) -> &String {
+    pub fn status(&self) -> &StatusCode {
         &self.status
     }
 
-    pub fn version(&self) -> &String {
+    pub fn version(&self) -> &Version {
         &self.version
     }
 
-    pub fn headers(&self) -> &HashMap<String, String> {
+    pub fn headers(&self) -> &HeaderMap {
         &self.headers
     }
 
-    pub fn body(&self) -> &String {
+    pub fn body(&self) -> &Bytes {
         &self.body
     }
 


Test patch: