@@ -285,9 +285,11 @@ struct FetchResponse {
285285#[ derive( Clone ) ]
286286pub struct TauriFetchController ( Arc < TauriFetchControllerInner > ) ;
287287
288+ const PENDING_REQUEST_MAX_AGE : Duration = Duration :: from_secs ( 120 ) ;
289+
288290struct TauriFetchControllerInner {
289291 app : AppHandle ,
290- pending : Mutex < HashMap < u64 , oneshot:: Sender < FetchResponse > > > ,
292+ pending : Mutex < HashMap < u64 , ( std :: time :: Instant , oneshot:: Sender < FetchResponse > ) > > ,
291293 chunks : Mutex < HashMap < u64 , ChunkBuffer > > ,
292294 nav_waiters : Mutex < Vec < oneshot:: Sender < ( ) > > > ,
293295 drive_lock : AsyncMutex < ( ) > ,
@@ -311,6 +313,16 @@ impl TauriFetchController {
311313 self . 0 . chunks . lock ( ) . remove ( & req_id) ;
312314 }
313315
316+ fn sweep_stale_requests ( & self ) {
317+ let mut pending = self . 0 . pending . lock ( ) ;
318+ let before = pending. len ( ) ;
319+ pending. retain ( |_, ( created_at, _) | created_at. elapsed ( ) < PENDING_REQUEST_MAX_AGE ) ;
320+ if pending. len ( ) < before {
321+ let mut chunks = self . 0 . chunks . lock ( ) ;
322+ chunks. retain ( |req_id, _| pending. contains_key ( req_id) ) ;
323+ }
324+ }
325+
314326 pub fn deliver_result (
315327 & self ,
316328 req_id : u64 ,
@@ -320,7 +332,7 @@ impl TauriFetchController {
320332 ) {
321333 self . 0 . chunks . lock ( ) . remove ( & req_id) ;
322334 let sender = self . 0 . pending . lock ( ) . remove ( & req_id) ;
323- if let Some ( tx ) = sender {
335+ if let Some ( ( _ , tx ) ) = sender {
324336 let _ = tx. send ( FetchResponse { ok, value, error } ) ;
325337 }
326338 }
@@ -356,13 +368,13 @@ impl TauriFetchController {
356368 . and_then ( |v| v. as_str ( ) )
357369 . map ( |s| s. to_string ( ) ) ;
358370 let sender = self . 0 . pending . lock ( ) . remove ( & req_id) ;
359- if let Some ( tx ) = sender {
371+ if let Some ( ( _ , tx ) ) = sender {
360372 let _ = tx. send ( FetchResponse { ok, value, error } ) ;
361373 }
362374 }
363375 Err ( err) => {
364376 let sender = self . 0 . pending . lock ( ) . remove ( & req_id) ;
365- if let Some ( tx ) = sender {
377+ if let Some ( ( _ , tx ) ) = sender {
366378 let _ = tx. send ( FetchResponse {
367379 ok : false ,
368380 value : Value :: Null ,
@@ -437,9 +449,13 @@ impl TauriFetchController {
437449 . app
438450 . get_webview ( FETCH_WORKER_LABEL )
439451 . ok_or_else ( || anyhow:: anyhow!( "fetch_worker webview not available" ) ) ?;
452+ self . sweep_stale_requests ( ) ;
440453 let req_id = self . 0 . next_req_id . fetch_add ( 1 , Ordering :: SeqCst ) ;
441454 let ( tx, rx) = oneshot:: channel ( ) ;
442- self . 0 . pending . lock ( ) . insert ( req_id, tx) ;
455+ self . 0
456+ . pending
457+ . lock ( )
458+ . insert ( req_id, ( std:: time:: Instant :: now ( ) , tx) ) ;
443459 let payload = serde_json:: json!( {
444460 "reqId" : req_id,
445461 "kind" : kind,
@@ -480,10 +496,33 @@ impl TauriFetchController {
480496 }
481497}
482498
483- #[ async_trait]
484- impl FetchController for TauriFetchController {
485- async fn fetch ( & self , url : & str , timeout : Duration ) -> Result < FetchedPage > {
486- let _drive = self . 0 . drive_lock . lock ( ) . await ;
499+ impl TauriFetchController {
500+ fn park_worker_to_blank ( & self ) {
501+ let outcome = run_fetch_on_main_thread (
502+ & self . 0 . app ,
503+ "fetch_worker park" ,
504+ move |app| {
505+ let Some ( webview) = app. get_webview ( FETCH_WORKER_LABEL ) else {
506+ return Ok ( ( ) ) ;
507+ } ;
508+ if let Ok ( current) = webview. url ( ) {
509+ if current. as_str ( ) == ABOUT_BLANK {
510+ return Ok ( ( ) ) ;
511+ }
512+ }
513+ let parsed = Url :: parse ( ABOUT_BLANK )
514+ . map_err ( |e| anyhow:: anyhow!( "parse about:blank: {e}" ) ) ?;
515+ webview
516+ . navigate ( parsed)
517+ . map_err ( |e| anyhow:: anyhow!( "fetch_worker park navigate failed: {e}" ) )
518+ } ,
519+ ) ;
520+ if let Err ( err) = outcome {
521+ tracing:: warn!( "[fetch_worker] park to about:blank failed: {err}" ) ;
522+ }
523+ }
524+
525+ async fn fetch_inner ( & self , url : & str , timeout : Duration ) -> Result < FetchedPage > {
487526 ensure_fetch_webview ( & self . 0 . app ) ?;
488527
489528 let state = self
@@ -506,7 +545,7 @@ impl FetchController for TauriFetchController {
506545 . map_err ( |e| anyhow:: anyhow!( "fetch_worker navigate failed: {e}" ) )
507546 } ) ?;
508547
509- let nav_timeout = timeout. min ( Duration :: from_secs ( 45 ) ) ;
548+ let nav_timeout = timeout. min ( Duration :: from_secs ( 8 ) ) ;
510549 if let Err ( err) = self
511550 . await_nav_ready ( parsed. as_str ( ) , nav_timeout)
512551 . await
@@ -518,18 +557,18 @@ impl FetchController for TauriFetchController {
518557 . exec_internal (
519558 "wait_for_ready" ,
520559 serde_json:: json!( {
521- "ready_state" : "complete " ,
522- "timeout_ms" : 10_000 ,
560+ "ready_state" : "interactive " ,
561+ "timeout_ms" : 4_000 ,
523562 } ) ,
524- Duration :: from_millis ( 12_000 ) ,
563+ Duration :: from_millis ( 5_000 ) ,
525564 )
526565 . await ;
527566
528567 let resp = self
529568 . exec_internal (
530569 "extract_text" ,
531570 Value :: Null ,
532- Duration :: from_millis ( 10_000 ) ,
571+ Duration :: from_millis ( 8_000 ) ,
533572 )
534573 . await ?;
535574 if !resp. ok {
@@ -538,29 +577,56 @@ impl FetchController for TauriFetchController {
538577 resp. error. unwrap_or_default( )
539578 ) ) ;
540579 }
541- let url_out = resp
542- . value
580+ let mut page = page_from_extract_value ( & resp. value ) ;
581+ if page. text . chars ( ) . count ( ) < 200 {
582+ tokio:: time:: sleep ( Duration :: from_millis ( 1_200 ) ) . await ;
583+ if let Ok ( retry) = self
584+ . exec_internal (
585+ "extract_text" ,
586+ Value :: Null ,
587+ Duration :: from_millis ( 8_000 ) ,
588+ )
589+ . await
590+ {
591+ if retry. ok {
592+ let second = page_from_extract_value ( & retry. value ) ;
593+ if second. text . chars ( ) . count ( ) > page. text . chars ( ) . count ( ) {
594+ page = second;
595+ }
596+ }
597+ }
598+ }
599+ Ok ( page)
600+ }
601+ }
602+
603+ fn page_from_extract_value ( value : & Value ) -> FetchedPage {
604+ FetchedPage {
605+ url : value
543606 . get ( "url" )
544607 . and_then ( |v| v. as_str ( ) )
545608 . unwrap_or ( "" )
546- . to_string ( ) ;
547- let title = resp
548- . value
609+ . to_string ( ) ,
610+ title : value
549611 . get ( "title" )
550612 . and_then ( |v| v. as_str ( ) )
551613 . unwrap_or ( "" )
552- . to_string ( ) ;
553- let text = resp
554- . value
614+ . to_string ( ) ,
615+ text : value
555616 . get ( "text" )
556617 . and_then ( |v| v. as_str ( ) )
557618 . unwrap_or ( "" )
558- . to_string ( ) ;
559- Ok ( FetchedPage {
560- url : url_out,
561- title,
562- text,
563- } )
619+ . to_string ( ) ,
620+ }
621+ }
622+
623+ #[ async_trait]
624+ impl FetchController for TauriFetchController {
625+ async fn fetch ( & self , url : & str , timeout : Duration ) -> Result < FetchedPage > {
626+ let _drive = self . 0 . drive_lock . lock ( ) . await ;
627+ let result = self . fetch_inner ( url, timeout) . await ;
628+ self . park_worker_to_blank ( ) ;
629+ result
564630 }
565631}
566632
0 commit comments