mirror of
https://github.com/chatmail/core.git
synced 2026-08-18 06:36:29 +03:00
feat: webxdc: fallback to index.html if it exists when loading directory
This makes it easier for developers and users that port webapps or even vibecode normal websites. Those may rely on the webserver behavior of loading an index.html when navigating to a directory. Before this pr this did not work, with this pr we have a better out of the box experience. Still we may want to document that links ending in `dir/index.html` are more reliable than just linking to `dir/` - atleast in other host app implementations
This commit is contained in:
@@ -847,8 +847,11 @@ fn parse_webxdc_manifest(bytes: &[u8]) -> Result<WebxdcManifest> {
|
||||
}
|
||||
|
||||
async fn get_blob(archive: &mut SeekZipFileReader<BufReader<File>>, name: &str) -> Result<Vec<u8>> {
|
||||
let (i, _) =
|
||||
let (i, entry) =
|
||||
find_zip_entry(archive.file(), name).ok_or_else(|| anyhow!("no entry found for {name}"))?;
|
||||
if entry.dir()? {
|
||||
bail!("'{name}' is a directory not a file.")
|
||||
}
|
||||
let mut reader = archive.reader_with_entry(i).await?;
|
||||
let mut buf = Vec::new();
|
||||
reader.read_to_end_checked(&mut buf).await?;
|
||||
@@ -903,7 +906,28 @@ impl Message {
|
||||
));
|
||||
}
|
||||
|
||||
get_blob(&mut archive, name).await
|
||||
let result = get_blob(&mut archive, name).await;
|
||||
// not found and no extension, then assume directory and try index.html
|
||||
// this mimics how webservers behave.
|
||||
if result.is_err() && !name.contains('.') {
|
||||
let base = if name.ends_with('/') {
|
||||
name.to_string()
|
||||
} else {
|
||||
format!("{name}/")
|
||||
};
|
||||
// ignore first slash. So that requesting "" for index.html works
|
||||
let base = base.trim_start_matches('/');
|
||||
let fallbacks = [format!("{base}index.html"), format!("{base}index.htm")];
|
||||
for fallback in &fallbacks {
|
||||
let result = get_blob(&mut archive, fallback).await;
|
||||
if result.is_ok() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result // return orginal error to the path that was requested, not the fallback
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Return info from manifest.toml or from fallbacks.
|
||||
|
||||
@@ -1144,6 +1144,49 @@ async fn test_get_webxdc_blob_with_subdirs() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_get_webxdc_blob_indexhtml_fallback() -> Result<()> {
|
||||
let t = &TestContext::new_alice().await;
|
||||
let chat_id = create_group(t, "foo").await?;
|
||||
let instance = {
|
||||
let mut instance = create_webxdc_instance(
|
||||
t,
|
||||
"indexhtml-fallback.xdc",
|
||||
include_bytes!("../../test-data/webxdc/indexhtml-fallback.xdc"),
|
||||
)?;
|
||||
let instance_msg_id = send_msg(t, chat_id, &mut instance).await?;
|
||||
assert_eq!(instance.viewtype, Viewtype::Webxdc);
|
||||
Message::load_from_db(t, instance_msg_id).await?
|
||||
};
|
||||
|
||||
// "../" links that go back should work
|
||||
assert!(instance.get_webxdc_blob(t, "").await.is_ok());
|
||||
// test falling back to index.html
|
||||
assert!(instance.get_webxdc_blob(t, "/alpha").await.is_ok());
|
||||
assert!(instance.get_webxdc_blob(t, "/alpha/").await.is_ok());
|
||||
// test falling back to index.htm
|
||||
assert!(instance.get_webxdc_blob(t, "/beta").await.is_ok());
|
||||
assert!(instance.get_webxdc_blob(t, "/beta/").await.is_ok());
|
||||
// test that original error is still there when there is no index.htm(l) file
|
||||
assert!(instance.get_webxdc_blob(t, "/control").await.is_err());
|
||||
println!("{:?}", instance.get_webxdc_blob(t, "/control/").await);
|
||||
println!(
|
||||
"{:?}",
|
||||
instance.get_webxdc_blob(t, "/control/were.html").await
|
||||
);
|
||||
|
||||
assert!(instance.get_webxdc_blob(t, "/control/").await.is_err());
|
||||
assert!(
|
||||
!instance
|
||||
.get_webxdc_blob(t, "/control/")
|
||||
.await
|
||||
.expect_err("error expected because there is no index.html")
|
||||
.to_string()
|
||||
.contains("control/index.html")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_parse_webxdc_manifest() -> Result<()> {
|
||||
let result = parse_webxdc_manifest(r#"key = syntax error"#.as_bytes());
|
||||
|
||||
BIN
test-data/webxdc/indexhtml-fallback.xdc
Normal file
BIN
test-data/webxdc/indexhtml-fallback.xdc
Normal file
Binary file not shown.
Reference in New Issue
Block a user