Skip to content

Commit b2efd13

Browse files
committed
feat: Add dependencies, initialize cache, and implement caching mechanism
- Added dependencies for `serde_json`, `lru`, `select`, `log`, and `env_logger` - Initialized the logger and set up logging for the server running message and initial request URL - Implemented caching logic to check for content in cache, retrieve content from tab, and store content in cache - Implemented request timing to measure duration of requests
1 parent b37d68a commit b2efd13

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@ headless_chrome = { git = "https://github.com/rust-headless-chrome/rust-headless
88
warp = "0.3.7"
99
serde = { version = "1.0", features = ["derive"] }
1010
tokio = { version = "1", features = ["full"] }
11+
serde_json = "1.0"
12+
lru = "0.12"
13+
select = "0.5"
14+
log = "0.4"
15+
env_logger = "0.10"

src/main.rs

+51-5
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@ use tokio::sync::{Semaphore, Mutex};
55
use warp::Filter;
66
use serde::Deserialize;
77
use std::collections::VecDeque;
8+
use lru::LruCache;
9+
use tokio::time::{Duration, Instant};
10+
use std::num::NonZeroUsize;
11+
use std::env;
12+
use log::{info, error};
813

914
#[tokio::main]
1015
async fn main() {
16+
env_logger::init();
17+
1118
let args: Vec<&OsStr> = vec![
1219
OsStr::new("--no-sandbox"),
1320
OsStr::new("--disable-gpu"),
@@ -30,14 +37,23 @@ async fn main() {
3037
let semaphore = Arc::new(Semaphore::new(4));
3138
let tab_pool = Arc::new(Mutex::new(VecDeque::new()));
3239

40+
let cache_maxsize = env::var("CACHE_MAXSIZE")
41+
.unwrap_or_else(|_| "100".to_string())
42+
.parse::<usize>()
43+
.expect("CACHE_MAXSIZE must be a positive integer");
44+
45+
let cache_size = NonZeroUsize::new(cache_maxsize).expect("CACHE_MAXSIZE must be a non-zero integer");
46+
let cache = Arc::new(Mutex::new(LruCache::new(cache_size))); // Cache max size from environment variable
47+
3348
let render_route = warp::path("html")
3449
.and(warp::query::<RenderQuery>())
3550
.and(with_browser(browser.clone()))
3651
.and(with_semaphore(semaphore.clone()))
3752
.and(with_tab_pool(tab_pool.clone()))
53+
.and(with_cache(cache.clone()))
3854
.and_then(render_handler);
3955

40-
println!("Server running on http://0.0.0.0:8080");
56+
info!("Server running on http://0.0.0.0:8080");
4157
warp::serve(render_route).run(([0, 0, 0, 0], 8080)).await;
4258
}
4359

@@ -64,6 +80,12 @@ fn with_tab_pool(
6480
warp::any().map(move || tab_pool.clone())
6581
}
6682

83+
fn with_cache(
84+
cache: Arc<Mutex<LruCache<String, (String, Instant)>>>,
85+
) -> impl Filter<Extract = (Arc<Mutex<LruCache<String, (String, Instant)>>>,), Error = std::convert::Infallible> + Clone {
86+
warp::any().map(move || cache.clone())
87+
}
88+
6789
#[derive(Debug)]
6890
struct CustomError;
6991

@@ -74,32 +96,51 @@ async fn render_handler(
7496
browser: Arc<Browser>,
7597
semaphore: Arc<Semaphore>,
7698
tab_pool: Arc<Mutex<VecDeque<Arc<Tab>>>>,
99+
cache: Arc<Mutex<LruCache<String, (String, Instant)>>>,
77100
) -> Result<impl warp::Reply, warp::Rejection> {
78101
let _permit = semaphore.acquire().await;
79102

103+
let cache_ttl_secs = env::var("CACHE_TTL")
104+
.unwrap_or_else(|_| "60".to_string())
105+
.parse::<u64>()
106+
.expect("CACHE_TTL must be a positive integer");
107+
let cache_ttl = Duration::new(cache_ttl_secs, 0);
108+
109+
info!("Initial request to {}", query.url);
110+
111+
let start_time = Instant::now();
112+
113+
let mut cache_guard = cache.lock().await;
114+
if let Some((content, timestamp)) = cache_guard.get(&query.url) {
115+
if timestamp.elapsed() < cache_ttl {
116+
info!("Cache hit for {}", query.url);
117+
return Ok(warp::reply::html(content.clone()));
118+
}
119+
}
120+
80121
let tab = {
81122
let mut pool = tab_pool.lock().await;
82123
if let Some(tab) = pool.pop_front() {
83124
tab
84125
} else {
85126
browser.new_tab().map_err(|e| {
86-
eprintln!("Failed to create new tab: {:?}", e);
127+
error!("Failed to create new tab: {:?}", e);
87128
warp::reject::custom(CustomError)
88129
})?
89130
}
90131
};
91132

92133
tab.navigate_to(&query.url).map_err(|e| {
93-
eprintln!("Failed to navigate to URL: {:?}", e);
134+
error!("Failed to navigate to URL: {:?}", e);
94135
warp::reject::custom(CustomError)
95136
})?;
96137
tab.wait_until_navigated().map_err(|e| {
97-
eprintln!("Failed to wait until navigated: {:?}", e);
138+
error!("Failed to wait until navigated: {:?}", e);
98139
warp::reject::custom(CustomError)
99140
})?;
100141

101142
let content = tab.get_content().map_err(|e| {
102-
eprintln!("Failed to get content: {:?}", e);
143+
error!("Failed to get content: {:?}", e);
103144
warp::reject::custom(CustomError)
104145
})?;
105146

@@ -108,5 +149,10 @@ async fn render_handler(
108149
pool.push_back(Arc::clone(&tab));
109150
}
110151

152+
cache_guard.put(query.url.clone(), (content.clone(), Instant::now()));
153+
154+
let duration = start_time.elapsed();
155+
info!("Got {} in {:?} for {}", content.len(), duration, query.url);
156+
111157
Ok(warp::reply::html(content))
112158
}

0 commit comments

Comments
 (0)