@@ -494,6 +494,224 @@ where
494494 resp
495495}
496496
497+ /// Register a new path with the HTTP server. This will cause the HTTP server to
498+ /// forward any requests on this path to the calling process.
499+ ///
500+ /// Instead of binding at just a path, this function tells the HTTP server to
501+ /// generate a *subdomain* with our package ID (with non-ascii-alphanumeric
502+ /// characters converted to `-`) and bind at that subdomain.
503+ pub fn secure_bind_http_path < T > ( path : T ) -> std:: result:: Result < ( ) , HttpServerError >
504+ where
505+ T : Into < String > ,
506+ {
507+ let res = KiRequest :: to ( ( "our" , "http_server" , "distro" , "sys" ) )
508+ . body (
509+ serde_json:: to_vec ( & HttpServerAction :: SecureBind {
510+ path : path. into ( ) ,
511+ cache : false ,
512+ } )
513+ . unwrap ( ) ,
514+ )
515+ . send_and_await_response ( 5 )
516+ . unwrap ( ) ;
517+ let Ok ( Message :: Response { body, .. } ) = res else {
518+ return Err ( HttpServerError :: PathBindError {
519+ error : "http_server timed out" . to_string ( ) ,
520+ } ) ;
521+ } ;
522+ let Ok ( resp) = serde_json:: from_slice :: < std:: result:: Result < ( ) , HttpServerError > > ( & body) else {
523+ return Err ( HttpServerError :: PathBindError {
524+ error : "http_server gave unexpected response" . to_string ( ) ,
525+ } ) ;
526+ } ;
527+ resp
528+ }
529+
530+ /// Register a new path with the HTTP server, and serve a static file from it.
531+ /// The server will respond to GET requests on this path with the given file.
532+ ///
533+ /// Instead of binding at just a path, this function tells the HTTP server to
534+ /// generate a *subdomain* with our package ID (with non-ascii-alphanumeric
535+ /// characters converted to `-`) and bind at that subdomain.
536+ pub fn secure_bind_http_static_path < T > (
537+ path : T ,
538+ content_type : Option < String > ,
539+ content : Vec < u8 > ,
540+ ) -> std:: result:: Result < ( ) , HttpServerError >
541+ where
542+ T : Into < String > ,
543+ {
544+ let res = KiRequest :: to ( ( "our" , "http_server" , "distro" , "sys" ) )
545+ . body (
546+ serde_json:: to_vec ( & HttpServerAction :: SecureBind {
547+ path : path. into ( ) ,
548+ cache : true ,
549+ } )
550+ . unwrap ( ) ,
551+ )
552+ . blob ( KiBlob {
553+ mime : content_type,
554+ bytes : content,
555+ } )
556+ . send_and_await_response ( 5 )
557+ . unwrap ( ) ;
558+ let Ok ( Message :: Response { body, .. } ) = res else {
559+ return Err ( HttpServerError :: PathBindError {
560+ error : "http_server timed out" . to_string ( ) ,
561+ } ) ;
562+ } ;
563+ let Ok ( resp) = serde_json:: from_slice :: < std:: result:: Result < ( ) , HttpServerError > > ( & body) else {
564+ return Err ( HttpServerError :: PathBindError {
565+ error : "http_server gave unexpected response" . to_string ( ) ,
566+ } ) ;
567+ } ;
568+ resp
569+ }
570+
571+ /// Register a WebSockets path with the HTTP server. Your app must do this
572+ /// in order to receive incoming WebSocket connections.
573+ ///
574+ /// Instead of binding at just a path, this function tells the HTTP server to
575+ /// generate a *subdomain* with our package ID (with non-ascii-alphanumeric
576+ /// characters converted to `-`) and bind at that subdomain.
577+ pub fn secure_bind_ws_path < T > ( path : T , encrypted : bool ) -> std:: result:: Result < ( ) , HttpServerError >
578+ where
579+ T : Into < String > ,
580+ {
581+ let res = KiRequest :: to ( ( "our" , "http_server" , "distro" , "sys" ) )
582+ . body (
583+ serde_json:: to_vec ( & HttpServerAction :: WebSocketSecureBind {
584+ path : path. into ( ) ,
585+ encrypted,
586+ extension : false ,
587+ } )
588+ . unwrap ( ) ,
589+ )
590+ . send_and_await_response ( 5 )
591+ . unwrap ( ) ;
592+ let Ok ( Message :: Response { body, .. } ) = res else {
593+ return Err ( HttpServerError :: PathBindError {
594+ error : "http_server timed out" . to_string ( ) ,
595+ } ) ;
596+ } ;
597+ let Ok ( resp) = serde_json:: from_slice :: < std:: result:: Result < ( ) , HttpServerError > > ( & body) else {
598+ return Err ( HttpServerError :: PathBindError {
599+ error : "http_server gave unexpected response" . to_string ( ) ,
600+ } ) ;
601+ } ;
602+ resp
603+ }
604+
605+ /// Serve index.html
606+ ///
607+ /// Instead of binding at just a path, this function tells the HTTP server to
608+ /// generate a *subdomain* with our package ID (with non-ascii-alphanumeric
609+ /// characters converted to `-`) and bind at that subdomain.
610+ pub fn secure_serve_index_html (
611+ our : & Address ,
612+ directory : & str ,
613+ paths : Vec < & str > ,
614+ ) -> anyhow:: Result < ( ) > {
615+ KiRequest :: to ( ( "our" , "vfs" , "distro" , "sys" ) )
616+ . body ( serde_json:: to_vec ( & VfsRequest {
617+ path : format ! ( "/{}/pkg/{}/index.html" , our. package_id( ) , directory) ,
618+ action : VfsAction :: Read ,
619+ } ) ?)
620+ . send_and_await_response ( 5 ) ??;
621+
622+ let Some ( blob) = get_blob ( ) else {
623+ return Err ( anyhow:: anyhow!( "serve_index_html: no index.html blob" ) ) ;
624+ } ;
625+
626+ let index = String :: from_utf8 ( blob. bytes ) ?;
627+
628+ for path in paths {
629+ secure_bind_http_static_path (
630+ path,
631+ Some ( "text/html" . to_string ( ) ) ,
632+ index. to_string ( ) . as_bytes ( ) . to_vec ( ) ,
633+ ) ?;
634+ }
635+
636+ Ok ( ( ) )
637+ }
638+
639+ /// Serve static files from a given directory by binding all of them
640+ /// in http_server to their filesystem path.
641+ ///
642+ /// Instead of binding at just a path, this function tells the HTTP server to
643+ /// generate a *subdomain* with our package ID (with non-ascii-alphanumeric
644+ /// characters converted to `-`) and bind at that subdomain.
645+ pub fn secure_serve_ui ( our : & Address , directory : & str , paths : Vec < & str > ) -> anyhow:: Result < ( ) > {
646+ secure_serve_index_html ( our, directory, paths) ?;
647+
648+ let initial_path = format ! ( "{}/pkg/{}" , our. package_id( ) , directory) ;
649+
650+ let mut queue = VecDeque :: new ( ) ;
651+ queue. push_back ( initial_path. clone ( ) ) ;
652+
653+ while let Some ( path) = queue. pop_front ( ) {
654+ let Ok ( directory_response) = KiRequest :: to ( ( "our" , "vfs" , "distro" , "sys" ) )
655+ . body ( serde_json:: to_vec ( & VfsRequest {
656+ path,
657+ action : VfsAction :: ReadDir ,
658+ } ) ?)
659+ . send_and_await_response ( 5 ) ?
660+ else {
661+ return Err ( anyhow:: anyhow!( "serve_ui: no response for path" ) ) ;
662+ } ;
663+
664+ let directory_body = serde_json:: from_slice :: < VfsResponse > ( directory_response. body ( ) ) ?;
665+
666+ // Determine if it's a file or a directory and handle appropriately
667+ match directory_body {
668+ VfsResponse :: ReadDir ( directory_info) => {
669+ for entry in directory_info {
670+ match entry. file_type {
671+ // If it's a file, serve it statically
672+ FileType :: File => {
673+ KiRequest :: to ( ( "our" , "vfs" , "distro" , "sys" ) )
674+ . body ( serde_json:: to_vec ( & VfsRequest {
675+ path : entry. path . clone ( ) ,
676+ action : VfsAction :: Read ,
677+ } ) ?)
678+ . send_and_await_response ( 5 ) ??;
679+
680+ let Some ( blob) = get_blob ( ) else {
681+ return Err ( anyhow:: anyhow!(
682+ "serve_ui: no blob for {}" ,
683+ entry. path
684+ ) ) ;
685+ } ;
686+
687+ let content_type = get_mime_type ( & entry. path ) ;
688+
689+ secure_bind_http_static_path (
690+ entry. path . replace ( & initial_path, "" ) ,
691+ Some ( content_type) ,
692+ blob. bytes ,
693+ ) ?;
694+ }
695+ FileType :: Directory => {
696+ // Push the directory onto the queue
697+ queue. push_back ( entry. path ) ;
698+ }
699+ _ => { }
700+ }
701+ }
702+ }
703+ _ => {
704+ return Err ( anyhow:: anyhow!(
705+ "serve_ui: unexpected response for path: {:?}" ,
706+ directory_body
707+ ) )
708+ }
709+ } ;
710+ }
711+
712+ Ok ( ( ) )
713+ }
714+
497715/// Register a WebSockets path with the HTTP server to send and
498716/// receive system messages from a runtime extension. Only use
499717/// this if you are writing a runtime extension.
0 commit comments