1use crate::layers::LayerStack;
4use std::collections::BTreeMap;
5use std::time::{SystemTime, UNIX_EPOCH};
6
7#[derive(Debug)]
9pub struct Browser {
10 pub config: BrowserConfig,
11 pub layer_stack: LayerStack,
12 pub cache: BTreeMap<String, CachedResponse>,
13 pub cookies: BTreeMap<String, String>,
14 pub history: Vec<HistoryEntry>,
15}
16
17#[derive(Debug, Clone)]
19pub struct BrowserConfig {
20 pub user_agent: String,
21 pub enable_javascript: bool,
22 pub enable_cookies: bool,
23 pub enable_cache: bool,
24 pub max_redirects: usize,
25 pub timeout_ms: u64,
26
27 pub strict_ssl: bool,
29 pub block_trackers: bool,
30 pub block_advertisements: bool,
31 pub ephemeral_session_mode: bool,
32
33 pub num_layers: usize, pub tor_enabled: bool,
36 pub vpn_enabled: bool,
37 pub i2p_enabled: bool,
38 pub obfuscation_enabled: bool,
39}
40
41impl Default for BrowserConfig {
42 fn default() -> Self {
43 Self {
44 user_agent: "avx Browser/1.0".to_string(),
45 enable_javascript: false, enable_cookies: false,
47 enable_cache: true,
48 max_redirects: 5,
49 timeout_ms: 30_000,
50
51 strict_ssl: true,
52 block_trackers: true,
53 block_advertisements: true,
54 ephemeral_session_mode: true,
55
56 num_layers: 7,
57 tor_enabled: true,
58 vpn_enabled: true,
59 i2p_enabled: true,
60 obfuscation_enabled: true,
61 }
62 }
63}
64
65impl Browser {
66 pub fn new(config: BrowserConfig) -> Self {
68 let layer_stack = LayerStack::new(config.num_layers);
69
70 Self {
71 config,
72 layer_stack,
73 cache: BTreeMap::new(),
74 cookies: BTreeMap::new(),
75 history: Vec::new(),
76 }
77 }
78
79 pub fn navigate(&mut self, url: &str) -> Result<Response, BrowserError> {
81 let request = Request::parse(url)?;
83
84 if self.config.enable_cache {
86 if let Some(cached) = self.cache.get(url) {
87 if !cached.is_expired() {
88 return Ok(cached.response.clone());
89 }
90 }
91 }
92
93 let response = self.layer_stack.send_request(&request)?;
95
96 if self.config.enable_cache {
98 self.cache.insert(url.to_string(), CachedResponse {
99 response: response.clone(),
100 timestamp: current_timestamp(),
101 ttl: 3600, });
103 }
104
105 self.history.push(HistoryEntry {
107 url: url.to_string(),
108 title: response.title.clone(),
109 timestamp: current_timestamp(),
110 });
111
112 Ok(response)
113 }
114
115 pub fn clear_data(&mut self) {
117 self.cache.clear();
118 self.cookies.clear();
119 self.history.clear();
120 }
121
122 pub fn security_metrics(&self) -> SecurityMetrics {
124 SecurityMetrics {
125 layers_active: self.layer_stack.active_layers(),
126 anonymity_level: self.layer_stack.anonymity_level(),
127 latency_overhead_ms: self.layer_stack.total_latency(),
128 bandwidth_overhead: self.layer_stack.bandwidth_overhead(),
129 }
130 }
131}
132
133#[derive(Debug, Clone)]
135pub struct Request {
136 pub method: HttpMethod,
137 pub url: String,
138 pub headers: BTreeMap<String, String>,
139 pub body: Vec<u8>,
140}
141
142#[derive(Debug, Clone, Copy, PartialEq)]
143pub enum HttpMethod {
144 GET,
145 POST,
146 PUT,
147 DELETE,
148 HEAD,
149}
150
151impl Request {
152 pub fn parse(url: &str) -> Result<Self, BrowserError> {
153 if url.is_empty() {
154 return Err(BrowserError::InvalidUrl);
155 }
156
157 Ok(Self {
158 method: HttpMethod::GET,
159 url: url.to_string(),
160 headers: BTreeMap::new(),
161 body: Vec::new(),
162 })
163 }
164
165 pub fn add_header(&mut self, key: String, value: String) {
166 self.headers.insert(key, value);
167 }
168}
169
170#[derive(Debug, Clone)]
172pub struct Response {
173 pub status_code: u16,
174 pub headers: BTreeMap<String, String>,
175 pub body: Vec<u8>,
176 pub title: Option<String>,
177}
178
179impl Response {
180 pub fn ok(body: Vec<u8>) -> Self {
181 Self {
182 status_code: 200,
183 headers: BTreeMap::new(),
184 body,
185 title: None,
186 }
187 }
188
189 pub fn body_as_string(&self) -> String {
190 String::from_utf8_lossy(&self.body).to_string()
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct CachedResponse {
197 pub response: Response,
198 pub timestamp: u64,
199 pub ttl: u64,
200}
201
202impl CachedResponse {
203 pub fn is_expired(&self) -> bool {
204 current_timestamp() - self.timestamp > self.ttl
205 }
206}
207
208#[derive(Debug, Clone)]
210pub struct HistoryEntry {
211 pub url: String,
212 pub title: Option<String>,
213 pub timestamp: u64,
214}
215
216#[derive(Debug, Clone)]
218pub struct SecurityMetrics {
219 pub layers_active: usize,
220 pub anonymity_level: f64, pub latency_overhead_ms: u64,
222 pub bandwidth_overhead: f64, }
224
225#[derive(Debug)]
226pub enum BrowserError {
227 InvalidUrl,
228 NetworkError,
229 TimeoutError,
230 SslError,
231 LayerError(String),
232}
233
234fn current_timestamp() -> u64 {
235 SystemTime::now()
236 .duration_since(UNIX_EPOCH)
237 .unwrap()
238 .as_secs()
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 #[test]
246 fn test_browser_creation() {
247 let config = BrowserConfig::default();
248 let browser = Browser::new(config);
249
250 assert_eq!(browser.layer_stack.layers.len(), 7);
251 }
252
253 #[test]
254 fn test_request_parsing() {
255 let request = Request::parse("https://example.com").unwrap();
256 assert_eq!(request.method, HttpMethod::GET);
257 assert_eq!(request.url, "https://example.com");
258 }
259}
260
261
262
263
264