use cttps::CttpsStream;
use eframe::egui;
use tokio::net::TcpStream;
use std::sync::mpsc::{self, Receiver, Sender};
use tokio::runtime::Runtime;
struct CttpsBrowser {
url: String,
content: String,
logs: Vec<String>,
is_loading: bool,
runtime: Runtime,
log_tx: Sender<String>,
log_rx: Receiver<String>,
content_rx: Option<Receiver<String>>,
bridge_content: Vec<(String, String)>, bridge_receivers: Vec<Receiver<(String, String)>>,
}
impl CttpsBrowser {
fn new(cc: &eframe::CreationContext<'_>) -> Self {
cc.egui_ctx.set_visuals(egui::Visuals::dark());
let (log_tx, log_rx) = mpsc::channel();
Self {
url: "cttps://127.0.0.1:8080/".to_string(),
content: "Welcome to CTTPS Browser. Enter a URL to start securely browsing.".to_string(),
logs: vec!["Browser initialized.".to_string()],
is_loading: false,
runtime: Runtime::new().unwrap(),
log_tx,
log_rx,
content_rx: None,
bridge_content: Vec::new(),
bridge_receivers: Vec::new(),
}
}
fn fetch_url(&mut self) {
let url = self.url.clone();
let log_tx = self.log_tx.clone();
let (content_tx, content_rx) = mpsc::channel();
self.content_rx = Some(content_rx);
self.is_loading = true;
self.bridge_content.clear();
self.bridge_receivers.clear();
self.runtime.spawn(async move {
let _ = log_tx.send(format!("Parsing URL: {}", url));
let addr = url.trim_start_matches("cttps://").split('/').next().unwrap_or("127.0.0.1:8080");
let _ = log_tx.send(format!("Connecting to {}...", addr));
match tokio::time::timeout(std::time::Duration::from_secs(5), TcpStream::connect(addr)).await {
Ok(Ok(stream)) => {
let _ = log_tx.send("TCP Connected. Starting Handshake...".to_string());
match tokio::time::timeout(std::time::Duration::from_secs(5), CttpsStream::connect(stream)).await {
Ok(Ok(mut cttps)) => {
let _ = log_tx.send("CTTPS Handshake Success!".to_string());
let request = "GET / HTTP/1.1\r\n\r\n";
if let Err(e) = cttps.write_packet(request.as_bytes()).await {
let _ = log_tx.send(format!("Write Error: {}", e));
return;
}
let _ = log_tx.send("Request sent. Reading response...".to_string());
match tokio::time::timeout(std::time::Duration::from_secs(5), cttps.read_packet()).await {
Ok(Ok(data)) => {
let _ = log_tx.send(format!("Response received ({} bytes).", data.len()));
let response_text = String::from_utf8_lossy(&data).to_string();
let _ = content_tx.send(response_text);
}
Ok(Err(e)) => {
let _ = log_tx.send(format!("Protocol Error: {}", e));
let _ = content_tx.send(format!("Error: {}", e));
}
Err(_) => {
let _ = log_tx.send("Read Timeout".to_string());
let _ = content_tx.send("Error: Read Timeout".to_string());
}
}
}
Ok(Err(e)) => {
let _ = log_tx.send(format!("Handshake Error: {}", e));
let _ = content_tx.send(format!("Handshake Error: {}", e));
}
Err(_) => {
let _ = log_tx.send("Handshake Timeout".to_string());
let _ = content_tx.send("Handshake Timeout".to_string());
}
}
}
Ok(Err(e)) => {
let _ = log_tx.send(format!("Connection Failed: {}", e));
let _ = content_tx.send(format!("Connection Failed: {}", e));
}
Err(_) => {
let _ = log_tx.send("Connection Timeout".to_string());
let _ = content_tx.send("Connection Timeout".to_string());
}
}
});
}
fn check_for_bridge(&mut self) {
let content = self.content.clone();
let start_tag = "<https>";
let end_tag = "</https>";
let mut search_pos = 0;
while let Some(start_idx) = content[search_pos..].find(start_tag) {
let actual_start = search_pos + start_idx + start_tag.len();
if let Some(end_idx) = content[actual_start..].find(end_tag) {
let actual_end = actual_start + end_idx;
let https_url = content[actual_start..actual_end].to_string();
if !self.bridge_content.iter().any(|(u, _)| u == &https_url) {
self.fetch_bridge_url(https_url);
}
search_pos = actual_end + end_tag.len();
} else {
break;
}
}
}
fn fetch_bridge_url(&mut self, url: String) {
let log_tx = self.log_tx.clone();
let (tx, rx) = mpsc::channel();
let url_clone = url.clone();
self.runtime.spawn(async move {
let _ = log_tx.send(format!("Bridge: Fetching HTTPS content from {}...", url_clone));
match reqwest::get(&url_clone).await {
Ok(resp) => {
match resp.text().await {
Ok(text) => {
let _ = log_tx.send(format!("Bridge: Received {} bytes from HTTPS.", text.len()));
let _ = tx.send((url_clone, text));
}
Err(e) => { let _ = log_tx.send(format!("Bridge Error: {}", e)); }
}
}
Err(e) => { let _ = log_tx.send(format!("Bridge Network Error: {}", e)); }
}
});
self.bridge_receivers.push(rx);
}
}
impl eframe::App for CttpsBrowser {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
while let Ok(log) = self.log_rx.try_recv() {
self.logs.push(log);
}
if self.is_loading {
if let Some(rx) = &self.content_rx {
match rx.try_recv() {
Ok(content) => {
self.content = content;
self.is_loading = false;
self.logs.push("Page loaded successfully.".to_string());
self.check_for_bridge();
}
Err(mpsc::TryRecvError::Empty) => {}
Err(mpsc::TryRecvError::Disconnected) => {
self.is_loading = false;
self.logs.push("Error: Background task disconnected.".to_string());
self.content_rx = None;
}
}
}
}
let mut i = 0;
while i < self.bridge_receivers.len() {
match self.bridge_receivers[i].try_recv() {
Ok(data) => {
self.bridge_content.push(data);
self.bridge_receivers.remove(i);
}
Err(mpsc::TryRecvError::Empty) => {
i += 1;
}
Err(mpsc::TryRecvError::Disconnected) => {
self.bridge_receivers.remove(i);
}
}
}
egui::CentralPanel::default().show(ctx, |ui| {
ui.vertical_centered(|ui| {
ui.heading("🔒 CTTPS Secure Web Browser");
ui.add_space(5.0);
});
ui.horizontal(|ui| {
ui.label("URL:");
let edit = ui.add(egui::TextEdit::singleline(&mut self.url).desired_width(f32::INFINITY));
let enter_pressed = edit.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter));
if ui.button("Navigate").clicked() || enter_pressed {
self.logs.push(format!("Initiating request to: {}", self.url));
self.fetch_url();
}
});
ui.add_space(10.0);
ui.separator();
ui.columns(2, |columns| {
columns[0].vertical(|ui| {
ui.label(egui::RichText::new("Decrypted Page (HTML)").strong());
egui::ScrollArea::vertical().id_source("page").show(ui, |ui| {
ui.add(egui::TextEdit::multiline(&mut self.content).desired_width(f32::INFINITY).desired_rows(12));
if !self.bridge_content.is_empty() {
ui.add_space(20.0);
ui.label(egui::RichText::new("--- HTTPS Bridge Content ---").color(egui::Color32::from_rgb(255, 215, 0)));
for (url, content) in &self.bridge_content {
ui.group(|ui| {
ui.label(egui::RichText::new(format!("Source: {}", url)).italics().size(9.0).color(egui::Color32::LIGHT_BLUE));
ui.label(content);
});
}
}
});
});
columns[1].vertical(|ui| {
ui.label(egui::RichText::new("Security & Handshake Logs").strong().color(egui::Color32::GREEN));
egui::ScrollArea::vertical().id_source("logs").stick_to_bottom(true).show(ui, |ui| {
for log in &self.logs {
ui.label(egui::RichText::new(format!("> {}", log)).size(10.0).color(egui::Color32::LIGHT_GRAY));
}
});
});
});
});
ctx.request_repaint();
}
}
fn main() -> Result<(), eframe::Error> {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([1100.0, 700.0]),
..Default::default()
};
eframe::run_native(
"CTTPS Secure Browser",
options,
Box::new(|cc| Box::new(CttpsBrowser::new(cc))),
)
}