@@ -6,23 +6,22 @@ use warp::Filter;
6
6
use serde:: Deserialize ;
7
7
use std:: collections:: VecDeque ;
8
8
use lru:: LruCache ;
9
- use tokio:: time:: { Duration , Instant } ;
10
9
use std:: num:: NonZeroUsize ;
11
- use std:: env;
12
- use log:: { info, error} ;
10
+ use std:: time:: { Duration , Instant } ;
11
+ use std:: collections:: hash_map:: DefaultHasher ;
12
+ use std:: hash:: { Hash , Hasher } ;
13
+
14
+ const CACHE_TTL : Duration = Duration :: from_secs ( 600 ) ; // 10 minutes
13
15
14
16
#[ tokio:: main]
15
17
async fn main ( ) {
16
- env_logger:: init ( ) ;
17
-
18
18
let args: Vec < & OsStr > = vec ! [
19
19
OsStr :: new( "--no-sandbox" ) ,
20
20
OsStr :: new( "--disable-gpu" ) ,
21
21
OsStr :: new( "--disable-dev-shm-usage" ) ,
22
22
OsStr :: new( "--headless" ) ,
23
23
OsStr :: new( "--disable-software-rasterizer" ) ,
24
24
OsStr :: new( "--no-zygote" ) ,
25
- OsStr :: new( "--single-process" ) ,
26
25
] ;
27
26
28
27
let browser = Arc :: new (
@@ -36,14 +35,7 @@ async fn main() {
36
35
37
36
let semaphore = Arc :: new ( Semaphore :: new ( 4 ) ) ;
38
37
let tab_pool = Arc :: new ( Mutex :: new ( VecDeque :: new ( ) ) ) ;
39
-
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
38
+ let cache = Arc :: new ( Mutex :: new ( LruCache :: new ( NonZeroUsize :: new ( 1000 ) . unwrap ( ) ) ) ) ;
47
39
48
40
let render_route = warp:: path ( "html" )
49
41
. and ( warp:: query :: < RenderQuery > ( ) )
@@ -53,7 +45,7 @@ async fn main() {
53
45
. and ( with_cache ( cache. clone ( ) ) )
54
46
. and_then ( render_handler) ;
55
47
56
- info ! ( "Server running on http://0.0.0.0:8080" ) ;
48
+ println ! ( "Server running on http://0.0.0.0:8080" ) ;
57
49
warp:: serve ( render_route) . run ( ( [ 0 , 0 , 0 , 0 ] , 8080 ) ) . await ;
58
50
}
59
51
@@ -81,8 +73,8 @@ fn with_tab_pool(
81
73
}
82
74
83
75
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 {
76
+ cache : Arc < Mutex < LruCache < u64 , ( String , Instant ) > > > ,
77
+ ) -> impl Filter < Extract = ( Arc < Mutex < LruCache < u64 , ( String , Instant ) > > > , ) , Error = std:: convert:: Infallible > + Clone {
86
78
warp:: any ( ) . map ( move || cache. clone ( ) )
87
79
}
88
80
@@ -96,63 +88,66 @@ async fn render_handler(
96
88
browser : Arc < Browser > ,
97
89
semaphore : Arc < Semaphore > ,
98
90
tab_pool : Arc < Mutex < VecDeque < Arc < Tab > > > > ,
99
- cache : Arc < Mutex < LruCache < String , ( String , Instant ) > > > ,
91
+ cache : Arc < Mutex < LruCache < u64 , ( String , Instant ) > > > ,
100
92
) -> Result < impl warp:: Reply , warp:: Rejection > {
101
93
let _permit = semaphore. acquire ( ) . await ;
102
94
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 ) ;
95
+ let hash = hash_url ( & query. url ) ;
108
96
109
- info ! ( "Initial request to {}" , query. url) ;
110
-
111
- let start_time = Instant :: now ( ) ;
112
-
97
+ // Check if the response is in the cache and if it's still valid.
113
98
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 ( ) ) ) ;
99
+ if let Some ( ( response, timestamp) ) = cache_guard. get ( & hash) {
100
+ if timestamp. elapsed ( ) < CACHE_TTL {
101
+ return Ok ( warp:: reply:: html ( response. clone ( ) ) ) ;
118
102
}
119
103
}
120
104
105
+ // If not, use headless_chrome to get the DOM.
106
+ drop ( cache_guard) ; // release the lock before using headless_chrome
121
107
let tab = {
122
108
let mut pool = tab_pool. lock ( ) . await ;
123
109
if let Some ( tab) = pool. pop_front ( ) {
124
110
tab
125
111
} else {
126
112
browser. new_tab ( ) . map_err ( |e| {
127
- error ! ( "Failed to create new tab: {:?}" , e) ;
113
+ eprintln ! ( "Failed to create new tab: {:?}" , e) ;
128
114
warp:: reject:: custom ( CustomError )
129
115
} ) ?
130
116
}
131
117
} ;
132
118
133
119
tab. navigate_to ( & query. url ) . map_err ( |e| {
134
- error ! ( "Failed to navigate to URL: {:?}" , e) ;
120
+ eprintln ! ( "Failed to navigate to URL: {:?}" , e) ;
135
121
warp:: reject:: custom ( CustomError )
136
122
} ) ?;
137
123
tab. wait_until_navigated ( ) . map_err ( |e| {
138
- error ! ( "Failed to wait until navigated: {:?}" , e) ;
124
+ eprintln ! ( "Failed to wait until navigated: {:?}" , e) ;
139
125
warp:: reject:: custom ( CustomError )
140
126
} ) ?;
141
127
128
+ // Wait for a bit to allow SPA content to load.
129
+ tokio:: time:: sleep ( Duration :: from_secs ( 3 ) ) . await ;
130
+
142
131
let content = tab. get_content ( ) . map_err ( |e| {
143
- error ! ( "Failed to get content: {:?}" , e) ;
132
+ eprintln ! ( "Failed to get content: {:?}" , e) ;
144
133
warp:: reject:: custom ( CustomError )
145
134
} ) ?;
146
135
136
+ {
137
+ let mut cache_guard = cache. lock ( ) . await ;
138
+ cache_guard. put ( hash, ( content. clone ( ) , Instant :: now ( ) ) ) ;
139
+ }
140
+
147
141
{
148
142
let mut pool = tab_pool. lock ( ) . await ;
149
143
pool. push_back ( Arc :: clone ( & tab) ) ;
150
144
}
151
145
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
-
157
146
Ok ( warp:: reply:: html ( content) )
158
- }
147
+ }
148
+
149
+ fn hash_url ( url : & str ) -> u64 {
150
+ let mut hasher = DefaultHasher :: new ( ) ;
151
+ url. hash ( & mut hasher) ;
152
+ hasher. finish ( )
153
+ }
0 commit comments