char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f4a0f5b
main
1use std:: collections:: HashMap ; 2use std:: convert:: Infallible ; 3use std:: io; 4use std:: sync:: atomic::{ AtomicU64 , Ordering }; 5use std:: sync::{ Arc , Mutex }; 6 7use avril_caps:: Capabilities ; 8use avril_core:: Op ; 9use avril_core:: serde_v8:: ToJsBuffer ; 10use avril_http:: Engine ; 11use avril_http:: file::{ CheckFile , FileBody , HeaderPairs , header_pairs, headers, serve_file}; 12use serde:: Deserialize ; 13use tokio:: runtime:: Handle ; 14 15use avril_ext_file::{ Files as Handles , response_file}; 16use avril_host:: io:: response; 17 18# [ derive ( Deserialize )] 19# [ serde ( untagged )] 20enum Source { 21Path ( String ), 22Handle ( u64 ), 23} 24 25type HandleFile =( String , std:: fs:: File , CheckFile ); 26type Prepare =( String , HeaderPairs , Source , HeaderPairs ); 27type Prepared =( u16 , HeaderPairs , Option < u64 >); 28 29const MAX_ID : u64 =9_007_199_254_740_991 ; 30 31struct Files { 32runtime : Handle , 33caps : Arc < Capabilities >, 34bodies : Mutex < HashMap < u64 , Arc < FileBody >>>, 35sources : Mutex < HashMap < u64 , HandleFile >>, 36next : AtomicU64 , 37} 38 39impl Files { 40fn next_id ( & self ) -> io:: Result < u64 > { 41self . next 42. fetch_update ( Ordering :: AcqRel , Ordering :: Acquire , |id|{ 43( id <=MAX_ID ). then_some ( id +1 ) 44}) 45. map_err ( |_| io:: Error :: other ( "File response identifiers exhausted" )) 46} 47 48fn take ( & self , id : u64 ) -> io:: Result < Arc < FileBody >> { 49self . bodies 50. lock () 51. unwrap () 52. remove ( & id) 53. ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: NotConnected , "File response is closed" )) 54} 55 56fn prepare ( 57self : & Arc < Self >, 58( method, request, source, extra): Prepare , 59) ->impl Future < Output = io:: Result < Prepared >> +use <>{ 60let files =self . clone (); 61let source =match source{ 62Source :: Path ( path) =>Ok (( path, None )), 63Source :: Handle ( id) =>self 64. sources 65. lock () 66. unwrap () 67. remove ( & id) 68. map ( |( name, file, check) |( name, Some (( file, check)))) 69. ok_or_else ( ||{ 70 io:: Error :: new ( io:: ErrorKind :: NotConnected , "File source is closed" ) 71}), 72}; 73let pending =self . runtime . spawn_blocking ( move ||{ 74let ( path, opened) = source?; 75let response =serve_file ( 76 files. runtime . clone (), 77& method, 78headers ( request) ?, 79& path, 80headers ( extra) ?, 81 ||match opened{ 82Some ( opened) =>Ok ( opened), 83None =>response_file ( files. caps . clone (), path. clone ()), 84}, 85) ?; 86let status = response. status (). as_u16 (); 87let headers =header_pairs ( response. headers ()); 88let id =if let Some ( body) = response. into_body () { 89let id = files. next_id () ?; 90 files. bodies . lock (). unwrap (). insert ( id, body); 91Some ( id) 92} else { 93None 94}; 95Ok (( status, headers, id)) 96}); 97async move { pending. await . map_err ( io:: Error :: other) ?} 98} 99} 100 101impl Drop for Files { 102fn drop ( & mut self ) { 103for ( _, body) in self . bodies . get_mut (). unwrap (). drain () { 104 body. close (); 105} 106let sources = std:: mem:: take ( self . sources . get_mut (). unwrap ()); 107if !sources. is_empty () { 108self . runtime . spawn_blocking ( move ||drop ( sources)); 109} 110} 111} 112 113pub fn ops ( 114runtime : Handle , 115caps : Arc < Capabilities >, 116handles : Arc < Handles >, 117engine : Arc < Engine >, 118) ->Vec < Op > { 119let files =Arc :: new ( Files { 120 runtime, 121 caps, 122bodies : Mutex :: new ( HashMap :: new ()), 123sources : Mutex :: new ( HashMap :: new ()), 124next : AtomicU64 :: new ( 1 ), 125}); 126let acquire = files. clone (); 127let prepare = files. clone (); 128let read = files. clone (); 129let send = files. clone (); 130vec! [ 131Op :: new ( "op_http_file_handle" , move |( id, consume): ( u64 , bool ) |{ 132let acquired =( ||{ 133let token = acquire. next_id () ?; 134let source = handles. response_handle ( id, consume) ?; 135 acquire. sources . lock (). unwrap (). insert ( token, source); 136Ok ( token) 137})(); 138Ok ::< _ , Infallible >( response ( acquired)) 139}), 140Op :: asynchronous ( "op_http_serve_file" , move |args : Prepare |{ 141let pending = prepare. prepare ( args); 142async move { Ok ::< _ , Infallible >( response ( pending. await )) } 143}), 144Op :: asynchronous ( "op_http_file_read" , move |id : u64 |{ 145let body = read. bodies . lock (). unwrap (). get ( & id). cloned (); 146async move { 147let result =match body{ 148Some ( body) => body 149. read () 150. await 151. map ( |( bytes, done) |( ToJsBuffer :: from ( bytes), done)), 152None =>Err ( io:: Error :: new ( 153 io:: ErrorKind :: NotConnected , 154"File response is closed" , 155)), 156}; 157Ok ::< _ , Infallible >( response ( result)) 158} 159}), 160Op :: new ( "op_http_file_close" , move |id : u64 |{ 161if let Ok ( body) = files. take ( id) { 162 body. close (); 163} 164Ok ::< _ , Infallible >(()) 165}), 166Op :: new ( 167"op_http_respond_file" , 168move |( id, status, headers, body): ( u64 , u16 , HeaderPairs , u64 ) |{ 169let file = send. take ( body) ?; 170 engine 171. respond_file ( id, status, headers, file) 172. map_err ( io:: Error :: other) 173}, 174), 175] 176}