Skip to content

Commit 96dd661

Browse files
committed
feat(ext/fetch): support fetching local files
Closes denoland#11925 Closes denoland#2150
1 parent 56d9a02 commit 96dd661

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-1
lines changed

cli/tests/unit/fetch_test.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ unitTest(
2323
unitTest({ permissions: { net: true } }, async function fetchProtocolError() {
2424
await assertRejects(
2525
async () => {
26-
await fetch("file:///");
26+
await fetch("ftp://localhost:21/a/file");
2727
},
2828
TypeError,
2929
"not supported",
@@ -1360,3 +1360,59 @@ unitTest(
13601360
client.close();
13611361
},
13621362
);
1363+
1364+
unitTest(async function fetchFilePerm() {
1365+
await assertRejects(async () => {
1366+
await fetch(new URL("../testdata/subdir/json_1.json", import.meta.url));
1367+
}, Deno.errors.PermissionDenied);
1368+
});
1369+
1370+
unitTest(async function fetchFilePermDoesNotExist() {
1371+
await assertRejects(async () => {
1372+
await fetch(new URL("./bad.json", import.meta.url));
1373+
}, Deno.errors.PermissionDenied);
1374+
});
1375+
1376+
unitTest(
1377+
{ permissions: { read: true } },
1378+
async function fetchFileBadMethod() {
1379+
await assertRejects(
1380+
async () => {
1381+
await fetch(
1382+
new URL("../testdata/subdir/json_1.json", import.meta.url),
1383+
{
1384+
method: "POST",
1385+
},
1386+
);
1387+
},
1388+
TypeError,
1389+
"Fetching files only supports the GET method. Received POST.",
1390+
);
1391+
},
1392+
);
1393+
1394+
unitTest(
1395+
{ permissions: { read: true } },
1396+
async function fetchFileDoesNotExist() {
1397+
await assertRejects(
1398+
async () => {
1399+
await fetch(new URL("./bad.json", import.meta.url));
1400+
},
1401+
TypeError,
1402+
);
1403+
},
1404+
);
1405+
1406+
unitTest(
1407+
{ permissions: { read: true } },
1408+
async function fetchFile() {
1409+
const res = await fetch(
1410+
new URL("../testdata/subdir/json_1.json", import.meta.url),
1411+
);
1412+
assert(res.ok);
1413+
const fixture = await Deno.readTextFile(
1414+
"cli/tests/testdata/subdir/json_1.json",
1415+
);
1416+
assertEquals(await res.text(), fixture);
1417+
},
1418+
);

ext/fetch/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,36 @@ where
164164
// Check scheme before asking for net permission
165165
let scheme = url.scheme();
166166
let (request_rid, request_body_rid, cancel_handle_rid) = match scheme {
167+
"file" => {
168+
let permissions = state.borrow_mut::<FP>();
169+
let path = url
170+
.to_file_path()
171+
.map_err(|_| type_error(format!("Invalid file URL: {}", url)))?;
172+
permissions.check_read(&path)?;
173+
if method != Method::GET {
174+
return Err(type_error(format!(
175+
"Fetching files only supports the GET method. Received {}.",
176+
method
177+
)));
178+
}
179+
if !path.is_file() {
180+
return Err(type_error(format!("Unable to fetch \"{}\".", url)));
181+
}
182+
183+
let body = std::fs::read(&path)?;
184+
185+
let response = http::Response::builder()
186+
.status(http::StatusCode::OK)
187+
.body(reqwest::Body::from(body))?;
188+
189+
let fut = async move { Ok(Ok(Response::from(response))) };
190+
191+
let request_rid = state
192+
.resource_table
193+
.add(FetchRequestResource(Box::pin(fut)));
194+
195+
(request_rid, None, None)
196+
}
167197
"http" | "https" => {
168198
let permissions = state.borrow_mut::<FP>();
169199
permissions.check_net_url(&url)?;

0 commit comments

Comments
 (0)