summaryrefslogtreecommitdiff
path: root/ext/web
diff options
context:
space:
mode:
authorAndreu Botella <abb@randomunok.com>2021-09-08 11:29:21 +0200
committerGitHub <noreply@github.com>2021-09-08 11:29:21 +0200
commit1563088f060b1f1d4826bb3a6d4120b276a4e15f (patch)
treeff8b406511f0e7322a55e32d5272298ea4a061b5 /ext/web
parente07f28d301b990ebf534cbb8d5fa9f507475c89f (diff)
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for `blob:` URLs is populated during parsing to contain a reference to the `Blob` object that backs that object URL. It is this blob URL entry that the `fetch` API uses to resolve an object URL. Therefore, since the `Request` constructor parses URL inputs, it will have an associated blob URL entry which will be used when fetching, even if the object URL has been revoked since the construction of the `Request` object. (The `Request` constructor takes the URL as a string and parses it, so the object URL must be live at the time it is called.) This PR adds a new `blobFromObjectUrl` JS function (backed by a new `op_blob_from_object_url` op) that, if the URL is a valid object URL, returns a new `Blob` object whose parts are references to the same Rust `BlobPart`s used by the original `Blob` object. It uses this function to add a new `blobUrlEntry` field to inner requests, which will be `null` or such a `Blob`, and then uses `Blob.prototype.stream()` as the response's body. As a result of this, the `blob:` URL resolution from `op_fetch` is now useless, and has been removed.
Diffstat (limited to 'ext/web')
-rw-r--r--ext/web/09_file.js44
-rw-r--r--ext/web/blob.rs45
-rw-r--r--ext/web/internal.d.ts1
-rw-r--r--ext/web/lib.rs2
4 files changed, 88 insertions, 4 deletions
diff --git a/ext/web/09_file.js b/ext/web/09_file.js
index 43d9ffcb3..64cdb7b8a 100644
--- a/ext/web/09_file.js
+++ b/ext/web/09_file.js
@@ -167,11 +167,12 @@
return bag;
}
+ const _type = Symbol("Type");
const _size = Symbol("Size");
const _parts = Symbol("Parts");
class Blob {
- #type = "";
+ [_type] = "";
[_size] = 0;
[_parts];
@@ -199,7 +200,7 @@
this[_parts] = parts;
this[_size] = size;
- this.#type = normalizeType(options.type);
+ this[_type] = normalizeType(options.type);
}
/** @returns {number} */
@@ -211,7 +212,7 @@
/** @returns {string} */
get type() {
webidl.assertBranded(this, Blob);
- return this.#type;
+ return this[_type];
}
/**
@@ -561,7 +562,44 @@
}
}
+ /**
+ * Construct a new Blob object from an object URL.
+ *
+ * This new object will not duplicate data in memory with the original Blob
+ * object from which this URL was created or with other Blob objects created
+ * from the same URL, but they will be different objects.
+ *
+ * The object returned from this function will not be a File object, even if
+ * the original object from which the object URL was constructed was one. This
+ * means that the `name` and `lastModified` properties are lost.
+ *
+ * @param {string} url
+ * @returns {Blob | null}
+ */
+ function blobFromObjectUrl(url) {
+ const blobData = core.opSync("op_blob_from_object_url", url);
+ if (blobData === null) {
+ return null;
+ }
+
+ /** @type {BlobReference[]} */
+ const parts = [];
+ let totalSize = 0;
+
+ for (const { uuid, size } of blobData.parts) {
+ ArrayPrototypePush(parts, new BlobReference(uuid, size));
+ totalSize += size;
+ }
+
+ const blob = webidl.createBranded(Blob);
+ blob[_type] = blobData.media_type;
+ blob[_size] = totalSize;
+ blob[_parts] = parts;
+ return blob;
+ }
+
window.__bootstrap.file = {
+ blobFromObjectUrl,
getParts,
Blob,
File,
diff --git a/ext/web/blob.rs b/ext/web/blob.rs
index 2179de4a6..37e93c853 100644
--- a/ext/web/blob.rs
+++ b/ext/web/blob.rs
@@ -3,7 +3,7 @@ use deno_core::error::type_error;
use deno_core::parking_lot::Mutex;
use deno_core::url::Url;
use deno_core::ZeroCopyBuf;
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
@@ -260,3 +260,46 @@ pub fn op_blob_revoke_object_url(
blob_store.remove_object_url(&url);
Ok(())
}
+
+#[derive(Serialize)]
+pub struct ReturnBlob {
+ pub media_type: String,
+ pub parts: Vec<ReturnBlobPart>,
+}
+
+#[derive(Serialize)]
+pub struct ReturnBlobPart {
+ pub uuid: Uuid,
+ pub size: usize,
+}
+
+pub fn op_blob_from_object_url(
+ state: &mut deno_core::OpState,
+ url: String,
+ _: (),
+) -> Result<Option<ReturnBlob>, AnyError> {
+ let url = Url::parse(&url)?;
+ if url.scheme() != "blob" {
+ return Ok(None);
+ }
+
+ let blob_store = state.try_borrow::<BlobStore>().ok_or_else(|| {
+ type_error("Blob URLs are not supported in this context.")
+ })?;
+ if let Some(blob) = blob_store.get_object_url(url)? {
+ let parts = blob
+ .parts
+ .iter()
+ .map(|part| ReturnBlobPart {
+ uuid: blob_store.insert_part(part.clone()),
+ size: part.size(),
+ })
+ .collect();
+ Ok(Some(ReturnBlob {
+ media_type: blob.media_type.clone(),
+ parts,
+ }))
+ } else {
+ Ok(None)
+ }
+}
diff --git a/ext/web/internal.d.ts b/ext/web/internal.d.ts
index 6e72c1a80..94fc9c196 100644
--- a/ext/web/internal.d.ts
+++ b/ext/web/internal.d.ts
@@ -73,6 +73,7 @@ declare namespace globalThis {
};
declare var file: {
+ blobFromObjectUrl(url: string): Blob | null;
getParts(blob: Blob): string[];
Blob: typeof Blob;
File: typeof File;
diff --git a/ext/web/lib.rs b/ext/web/lib.rs
index 87ae46f2f..5f8ab1625 100644
--- a/ext/web/lib.rs
+++ b/ext/web/lib.rs
@@ -29,6 +29,7 @@ use std::usize;
use crate::blob::op_blob_create_object_url;
use crate::blob::op_blob_create_part;
+use crate::blob::op_blob_from_object_url;
use crate::blob::op_blob_read_part;
use crate::blob::op_blob_remove_part;
use crate::blob::op_blob_revoke_object_url;
@@ -88,6 +89,7 @@ pub fn init(blob_store: BlobStore, maybe_location: Option<Url>) -> Extension {
"op_blob_revoke_object_url",
op_sync(op_blob_revoke_object_url),
),
+ ("op_blob_from_object_url", op_sync(op_blob_from_object_url)),
(
"op_message_port_create_entangled",
op_sync(op_message_port_create_entangled),