Skip to content

Commit 7820481

Browse files
committed
add secure_bind methods to http
1 parent 830a86c commit 7820481

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed

src/http.rs

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)