char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
495c9a5
main
1mod file; 2 3use std:: io; 4use std:: sync:: atomic::{ AtomicBool , Ordering }; 5use std:: sync::{ Arc , Mutex }; 6 7use avril_caps::{ Capabilities , PathTarget }; 8use avril_core:: serde_v8::{ JsBuffer , ToJsBuffer }; 9use avril_core::{ Extension , Op }; 10use avril_http::{ Address , Authorize , Engine }; 11use bytes:: Bytes ; 12 13use avril_ext_websocket:: Sockets ; 14 15type RespondArgs =( u64 , u16 , Vec <( String , String )>, Option < JsBuffer >, bool ); 16 17pub fn init ( 18runtime : tokio:: runtime:: Handle , 19caps : Arc < Capabilities >, 20sockets : Arc < Sockets >, 21files : Arc < avril_ext_file:: Files >, 22cli : Option < Address >, 23) ->Extension { 24let engine =Arc :: new ( Engine :: new ( runtime. clone ())); 25let file_ops = file:: ops ( runtime. clone (), caps. clone (), files, engine. clone ()); 26let cli =Mutex :: new ( cli); 27let listen = engine. clone (); 28let host = engine. clone (); 29let next = engine. clone (); 30let finished = engine. clone (); 31let stop = engine. clone (); 32let read = engine. clone (); 33let respond = engine. clone (); 34let write = engine. clone (); 35let wait = engine. clone (); 36let upgrade = engine. clone (); 37let mut extension =Extension { 38name : "ext:http" , 39dependencies : & [ 40"ext:http_types" , 41"ext:file" , 42"ext:web" , 43"ext:websocket" , 44"ext:webstreams" , 45], 46ops : vec! [ 47Op :: new ( "op_http_listen" , move |mut address : Address |{ 48let caps = caps. clone (); 49let authorize: Authorize =match & mut address{ 50Address :: Tcp { hostname, ..} =>{ 51let hostname = hostname. clone (); 52Arc :: new ( move |address|{ 53let Address :: Tcp { port, ..} = addresselse { 54return Err ( "Invalid listener transport" . into ()); 55}; 56 caps. check_net ( & hostname, * port). map_err ( Into :: into) 57}) 58} 59Address :: Unix { path} =>{ 60let label = path 61. as_ref () 62. ok_or_else ( || io:: Error :: other ( "Missing Unix socket path" )) ?; 63if label. is_empty () { 64return Err ( io:: Error :: other ( "Empty Unix socket path" )); 65} 66let target =PathTarget :: for_entry ( caps. cwd (). join ( label)) ?; 67* path =Some ( 68 target 69. as_path () 70. to_str () 71. ok_or_else ( || io:: Error :: other ( "Unix socket paths must be UTF-8" )) ? 72. to_owned (), 73); 74Arc :: new ( move |_| caps. check_write ( & target). map_err ( Into :: into)) 75} 76}; 77 listen. listen ( address, authorize). map_err ( io:: Error :: other) 78}), 79Op :: with_scope ( "op_http_cli_listen" , move |scope, _: () |{ 80let address = cli 81. lock () 82. unwrap () 83. take () 84. ok_or_else ( || io:: Error :: other ( "No CLI listener configured" )) ?; 85let weak =Arc :: downgrade ( & host); 86let interrupted =Arc :: new ( AtomicBool :: new ( false )); 87let stopping = interrupted. clone (); 88let isolate = scope. thread_safe_handle (); 89let shutdown =Arc :: new ( move ||{ 90if let Some ( engine) = weak. upgrade () { 91let force = stopping. swap ( true , Ordering :: SeqCst ); 92 engine. stop_all ( force); 93if force{ 94 avril_ext_process:: Tree :: global (). force_live_processes (); 95 isolate. terminate_execution (); 96} 97} 98}); 99let interrupt = shutdown. clone (); 100# [ cfg ( unix )] 101// Serve owns SIGINT fate from here on through the shared 102// chaining registry: top-level listeners observe the 103// shutdown instead of being clobbered by it. 104 avril_ext_process:: Tree :: global (). claim_interrupt ( & runtime, interrupt) ?; 105# [ cfg ( not ( unix ))] 106 ctrlc:: set_handler ( move ||interrupt ()). map_err ( io:: Error :: other) ?; 107let bound = host 108. listen ( address, Arc :: new ( |_|Ok (()))) 109. map_err ( io:: Error :: other) ?; 110# [ cfg ( unix )] 111{ 112// Serve owns SIGTERM fate from here on: the subprocess 113// supervisor hands first deliveries to this shutdown 114// instead of stopping children itself. 115 avril_ext_process:: Tree :: global (). claim_term ( & runtime, shutdown) ?; 116} 117if interrupted. load ( Ordering :: SeqCst ) { 118 host. stop ( bound. 0 , true ); 119return Err ( io:: Error :: other ( "Server shutdown requested during binding" )); 120} 121match & bound. 1 { 122Address :: Tcp { hostname, port} =>eprintln! ( 123"Listening on http://{}:{port}" , 124if hostname. contains ( ':' ) { 125format! ( "[{hostname}]" ) 126} else { 127 hostname. clone () 128} 129), 130Address :: Unix { path} =>{ 131eprintln! ( "Listening on unix:{}" , path. as_deref (). unwrap_or_default ()) 132} 133} 134Ok ::< _ , io:: Error >( bound) 135}), 136Op :: asynchronous ( "op_http_next" , move |id : u64 | next. next ( id)), 137Op :: asynchronous ( "op_http_finished" , move |id : u64 | finished. finished ( id)), 138Op :: new ( "op_http_stop" , move |( id, force): ( u64 , bool ) |{ 139 stop. stop ( id, force); 140Ok ::< _ , io:: Error >(()) 141}), 142Op :: asynchronous ( "op_http_read" , move |id : u64 |{ 143let pending = read. read ( id); 144async move { 145// JS needs writable memory independent of Hyper's shared chunks. 146Ok ::< _ , io:: Error >( pending. await ?. map ( |bytes|ToJsBuffer :: from ( bytes. to_vec ()))) 147} 148}), 149Op :: new ( 150"op_http_respond" , 151move |( id, status, headers, bytes, streaming): RespondArgs |{ 152 respond 153. respond (( 154 id, 155 status, 156 headers, 157 bytes. map ( |bytes|Bytes :: copy_from_slice ( & bytes)), 158 streaming, 159)) 160. map_err ( io:: Error :: other) 161}, 162), 163Op :: asynchronous ( 164"op_http_write" , 165move |( id, bytes): ( u64 , Option < JsBuffer >) |{ 166 write. write ( id, bytes. map ( |bytes|Bytes :: copy_from_slice ( & bytes))) 167}, 168), 169Op :: asynchronous ( "op_http_wait" , move |id : u64 | wait. wait ( id)), 170Op :: new ( 171"op_http_upgrade" , 172move |( id, protocol): ( u64 , Option < String >) |{ 173let ( pending, headers) = upgrade 174. upgrade ( id, protocol. as_deref ()) 175. map_err ( io:: Error :: other) ?; 176let socket = sockets. accept ( pending, protocol. unwrap_or_default ()) ?; 177Ok ::< _ , io:: Error >(( socket, headers)) 178}, 179), 180Op :: new ( "op_http_cancel" , move |id : u64 |{ 181 engine. cancel ( id); 182Ok ::< _ , io:: Error >(()) 183}), 184], 185modules : & [ 186( 187"ext:http/main.js" , 188include_str! ( concat! ( env! ( "OUT_DIR" ), "/main.js" )), 189), 190( 191"ext:http/file.js" , 192include_str! ( concat! ( env! ( "OUT_DIR" ), "/file.js" )), 193), 194], 195entry_point : None , 196}; 197 extension. ops . extend ( file_ops); 198 extension 199}