Skip to content

Commit

Permalink
Added a polyfill for Url::to_file_path()
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael-F-Bryan committed May 29, 2023
1 parent 96becf3 commit e20bee2
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 10 deletions.
21 changes: 14 additions & 7 deletions lib/wasi/src/runtime/package_loader/builtin_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,20 @@ impl BuiltinPackageLoader {

async fn download(&self, dist: &DistributionInfo) -> Result<Bytes, Error> {
if dist.webc.scheme() == "file" {
// Note: The Url::to_file_path() method is platform-specific
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))]
if let Ok(path) = dist.webc.to_file_path() {
// FIXME: This will block the thread
let bytes = std::fs::read(&path)
.with_context(|| format!("Unable to read \"{}\"", path.display()))?;
return Ok(bytes.into());
match crate::runtime::resolver::utils::file_path_from_url(&dist.webc) {
Ok(path) => {
// FIXME: This will block the thread
let bytes = std::fs::read(&path)
.with_context(|| format!("Unable to read \"{}\"", path.display()))?;
return Ok(bytes.into());
}
Err(e) => {
tracing::debug!(
url=%dist.webc,
error=&*e,
"Unable to convert the file:// URL to a path",
);
}
}
}

Expand Down
45 changes: 42 additions & 3 deletions lib/wasi/src/runtime/resolver/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::Path;
use std::path::{Path, PathBuf};

use anyhow::Error;
use http::{HeaderMap, StatusCode};
use anyhow::{Context, Error};
use url::Url;

use crate::http::{HttpResponse, USER_AGENT};
Expand Down Expand Up @@ -56,13 +56,39 @@ pub(crate) fn http_error(response: &HttpResponse) -> Error {
Error::msg(status)
}

pub(crate) fn file_path_from_url(url: &Url) -> Result<PathBuf, Error> {
debug_assert_eq!(url.scheme(), "file");

// Note: The Url::to_file_path() method is platform-specific
cfg_if::cfg_if! {
if #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] {
if let Ok(path) = url.to_file_path() {
return Ok(path);
}

// Sometimes we'll get a UNC-like path (e.g.
// "file:///?\\C:/\\/path/to/file.txt") and Url::to_file_path()
// won't be able to handle the "\\?" so we try to "massage" the URL
// a bit.
// See <https://github.com/servo/rust-url/issues/450> for more.
let modified = url.as_str().replace(r"\\?", "").replace("//?", "").replace('\\', "/");
Url::parse(&modified)
.ok()
.and_then(|url| url.to_file_path().ok())
.context("Unable to extract the file path")
} else {
anyhow::bail!("Url::to_file_path() is not supported on this platform");
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
#[cfg(unix)]
fn behaviour_is_identical() {
fn from_file_path_behaviour_is_identical() {
let inputs = [
"/",
"/path",
Expand All @@ -78,4 +104,17 @@ mod tests {
assert_eq!(got, expected, "Mismatch for \"{path}\"");
}
}

#[test]
#[cfg(windows)]
fn to_file_path_can_handle_unc_paths() {
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
.canonicalize()
.unwrap();
let url = Url::from_file_path(&path).unwrap();

let got = file_path_from_url(&url).unwrap();

assert_eq!(got.canonicalize().unwrap(), path);
}
}

0 comments on commit e20bee2

Please sign in to comment.