1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-26 00:59:24 -05:00

feat(ext/web): add CompressionStream API (#11728)

Co-authored-by: Luca Casonato <hello@lcas.dev>
Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
This commit is contained in:
Leo Kettmeir 2022-01-24 18:03:06 +01:00 committed by GitHub
parent ae0414fa35
commit 30ddf436d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 324 additions and 32 deletions

65
Cargo.lock generated
View file

@ -316,9 +316,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
[[package]]
name = "brotli"
version = "3.3.2"
version = "3.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71cb90ade945043d3d53597b2fc359bb063db8ade2bcffe7997351d0756e9d50"
checksum = "f838e47a451d5a8fa552371f80024dd6ace9b7acdf25c4c3d0f9bc6816fb1c39"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@ -870,9 +870,9 @@ dependencies = [
[[package]]
name = "deno_doc"
version = "0.26.0"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ff82ae22a4012a843799f5baadda95ddc67cc3bed21fe5ebcda2c5cff768a1"
checksum = "e82f45d6513b789c04adf0915ca3c3c696f5f5f9379a82834ad4f783fbaa8b3a"
dependencies = [
"cfg-if 1.0.0",
"deno_ast",
@ -1074,6 +1074,7 @@ dependencies = [
"base64 0.13.0",
"deno_core",
"encoding_rs",
"flate2",
"serde",
"tokio",
"uuid",
@ -2150,9 +2151,9 @@ dependencies = [
[[package]]
name = "libloading"
version = "0.7.2"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
"cfg-if 1.0.0",
"winapi 0.3.9",
@ -3448,9 +3449,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.74"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142"
checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
dependencies = [
"indexmap",
"itoa 1.0.1",
@ -3545,9 +3546,9 @@ dependencies = [
[[package]]
name = "siphasher"
version = "0.3.7"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
checksum = "ba1eead9e94aa5a2e02de9e7839f96a007f686ae7a1d57c7797774810d24908a"
[[package]]
name = "slab"
@ -3778,9 +3779,9 @@ dependencies = [
[[package]]
name = "swc_ecma_ast"
version = "0.65.1"
version = "0.65.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "279a4595711a9aaf02165398d5f317b89d718ab90d30b2c08f34abda6545aa3a"
checksum = "3a82d26122365a721a7df46bd3e4240fbf19188d9ccf18c7faaa4d12dfc51488"
dependencies = [
"is-macro",
"num-bigint",
@ -3850,9 +3851,9 @@ dependencies = [
[[package]]
name = "swc_ecma_parser"
version = "0.87.0"
version = "0.87.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c454cbd95ab84fd9ffc5059cffad80b7711bd4b8732257a7c83f4b2c809dfa1"
checksum = "f032c57793a287a8b374e92a2b606e5eb890539285e4723fe6acb9be1fadcec6"
dependencies = [
"either",
"enum_kind",
@ -3870,9 +3871,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms"
version = "0.111.1"
version = "0.111.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae472031803e9fddb9f9141edc3ec0c3bff5904cd08e0509f2ad88689678ce7c"
checksum = "8dd7878bd017ed942fdcd5a892fa4990c21b1fe49c4aca7be1ecba16403e7052"
dependencies = [
"swc_atoms",
"swc_common",
@ -3889,9 +3890,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_base"
version = "0.56.0"
version = "0.56.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74824b2df0931ed3d84201e50138b68d1dc78a802aef9dbdec4ea0ffcea8a28b"
checksum = "d62c460e81027cdda2325348c1e5c0233c59127ab3227a45aaeac80eac7c5df1"
dependencies = [
"once_cell",
"phf",
@ -3909,9 +3910,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_classes"
version = "0.43.0"
version = "0.43.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a972a157b9104b7979a34556841f93e98d18d70b83c9e625b35dd390b57e59f"
checksum = "5cd6d1fbe53dfa365827eddadbafec687c7afecd294c54a62fedcd92c4e44293"
dependencies = [
"swc_atoms",
"swc_common",
@ -3936,9 +3937,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_optimization"
version = "0.81.0"
version = "0.81.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af299ad73dc689a0516950613a9262203feff76adc3f2f14307a12bdd5835c9"
checksum = "653395bad3ace0b3dcc99d9edf45bd08bbd97f48f8aec519d5b8ac336779ee00"
dependencies = [
"ahash",
"dashmap",
@ -3958,9 +3959,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_proposal"
version = "0.73.0"
version = "0.73.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe1cf222cfe757700ffea622589990b1b0ef0a18c85ffe6e8442ba515257aa98"
checksum = "da8cb3be65a35abfef0f4311d3f76dda381162a4920e526e3e7ac39d693df360"
dependencies = [
"either",
"serde",
@ -3978,9 +3979,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_react"
version = "0.75.0"
version = "0.75.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee01d3f5c0f424a199a4d13fc6fbe14cfd88ccff0b778558311075477e25310"
checksum = "4d9e590632bfd9b958f7b68f408040a320a95920021a241f1a403e6670f004cb"
dependencies = [
"ahash",
"base64 0.13.0",
@ -4003,9 +4004,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_typescript"
version = "0.77.1"
version = "0.77.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50560b92ecbd43ee4dea19ae763d0bde8d753c8401d76115c714cfb51f838e76"
checksum = "29fe8ca3b3ad557407449fcc653acde3026aa6078fa4307ae6b318a35514fd8f"
dependencies = [
"serde",
"swc_atoms",
@ -4048,9 +4049,9 @@ dependencies = [
[[package]]
name = "swc_ecmascript"
version = "0.108.2"
version = "0.108.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae93ca691a78c072b76045f70ffb9daf73e6e21530862c627c35a9a20bbd6d69"
checksum = "5feaed38d2e24849c1b1bb725ea455855ebe4bff52332a491cabc07d107db322"
dependencies = [
"swc_ecma_ast",
"swc_ecma_codegen",
@ -5109,9 +5110,9 @@ dependencies = [
[[package]]
name = "zeroize_derive"
version = "1.2.2"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73"
checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb"
dependencies = [
"proc-macro2 1.0.36",
"quote 1.0.14",

123
ext/web/14_compression.js Normal file
View file

@ -0,0 +1,123 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// @ts-check
/// <reference path="../../core/lib.deno_core.d.ts" />
/// <reference path="./internal.d.ts" />
/// <reference path="./lib.deno_web.d.ts" />
"use strict";
((window) => {
const core = window.Deno.core;
const webidl = window.__bootstrap.webidl;
const { TransformStream } = window.__bootstrap.streams;
webidl.converters.CompressionFormat = webidl.createEnumConverter(
"CompressionFormat",
[
"deflate",
"gzip",
],
);
class CompressionStream {
#transform;
constructor(format) {
const prefix = "Failed to construct 'CompressionStream'";
webidl.requiredArguments(arguments.length, 1, { prefix });
format = webidl.converters.CompressionFormat(format, {
prefix,
context: "Argument 1",
});
const rid = core.opSync("op_compression_new", format, false);
this.#transform = new TransformStream({
transform(chunk, controller) {
// TODO(lucacasonato): convert chunk to BufferSource
const output = core.opSync(
"op_compression_write",
rid,
chunk,
);
maybeEnqueue(controller, output);
},
flush(controller) {
const output = core.opSync("op_compression_finish", rid);
maybeEnqueue(controller, output);
},
});
this[webidl.brand] = webidl.brand;
}
get readable() {
webidl.assertBranded(this, CompressionStream);
return this.#transform.readable;
}
get writable() {
webidl.assertBranded(this, CompressionStream);
return this.#transform.writable;
}
}
webidl.configurePrototype(CompressionStream);
class DecompressionStream {
#transform;
constructor(format) {
const prefix = "Failed to construct 'DecompressionStream'";
webidl.requiredArguments(arguments.length, 1, { prefix });
format = webidl.converters.CompressionFormat(format, {
prefix,
context: "Argument 1",
});
const rid = core.opSync("op_compression_new", format, true);
this.#transform = new TransformStream({
transform(chunk, controller) {
// TODO(lucacasonato): convert chunk to BufferSource
const output = core.opSync(
"op_compression_write",
rid,
chunk,
);
maybeEnqueue(controller, output);
},
flush(controller) {
const output = core.opSync("op_compression_finish", rid);
maybeEnqueue(controller, output);
},
});
this[webidl.brand] = webidl.brand;
}
get readable() {
webidl.assertBranded(this, DecompressionStream);
return this.#transform.readable;
}
get writable() {
webidl.assertBranded(this, DecompressionStream);
return this.#transform.writable;
}
}
function maybeEnqueue(controller, output) {
if (output && output.byteLength > 0) {
controller.enqueue(output);
}
}
webidl.configurePrototype(DecompressionStream);
window.__bootstrap.compression = {
CompressionStream,
DecompressionStream,
};
})(globalThis);

View file

@ -18,6 +18,7 @@ async-trait = "0.1.51"
base64 = "0.13.0"
deno_core = { version = "0.116.0", path = "../../core" }
encoding_rs = "0.8.29"
flate2 = "1"
serde = "1.0.129"
tokio = { version = "1.10.1", features = ["full"] }
uuid = { version = "0.8.2", features = ["v4", "serde"] }

104
ext/web/compression.rs Normal file
View file

@ -0,0 +1,104 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use deno_core::error::AnyError;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
use flate2::write::GzDecoder;
use flate2::write::GzEncoder;
use flate2::write::ZlibDecoder;
use flate2::write::ZlibEncoder;
use flate2::Compression;
use std::borrow::Cow;
use std::cell::RefCell;
use std::io::Write;
use std::rc::Rc;
#[derive(Debug)]
struct CompressionResource(RefCell<Inner>);
#[derive(Debug)]
enum Inner {
DeflateDecoder(ZlibDecoder<Vec<u8>>),
DeflateEncoder(ZlibEncoder<Vec<u8>>),
GzDecoder(GzDecoder<Vec<u8>>),
GzEncoder(GzEncoder<Vec<u8>>),
}
impl Resource for CompressionResource {
fn name(&self) -> Cow<str> {
"compression".into()
}
}
pub fn op_compression_new(
state: &mut OpState,
format: String,
is_decoder: bool,
) -> Result<ResourceId, AnyError> {
let w = Vec::new();
let inner = match (format.as_str(), is_decoder) {
("deflate", true) => Inner::DeflateDecoder(ZlibDecoder::new(w)),
("deflate", false) => {
Inner::DeflateEncoder(ZlibEncoder::new(w, Compression::default()))
}
("gzip", true) => Inner::GzDecoder(GzDecoder::new(w)),
("gzip", false) => {
Inner::GzEncoder(GzEncoder::new(w, Compression::default()))
}
_ => unreachable!(),
};
let resource = CompressionResource(RefCell::new(inner));
Ok(state.resource_table.add(resource))
}
pub fn op_compression_write(
state: &mut OpState,
rid: ResourceId,
input: ZeroCopyBuf,
) -> Result<ZeroCopyBuf, AnyError> {
let resource = state.resource_table.get::<CompressionResource>(rid)?;
let mut inner = resource.0.borrow_mut();
let out: Vec<u8> = match &mut *inner {
Inner::DeflateDecoder(d) => {
d.write_all(&input)?;
d.flush()?;
d.get_mut().drain(..)
}
Inner::DeflateEncoder(d) => {
d.write_all(&input)?;
d.flush()?;
d.get_mut().drain(..)
}
Inner::GzDecoder(d) => {
d.write_all(&input)?;
d.flush()?;
d.get_mut().drain(..)
}
Inner::GzEncoder(d) => {
d.write_all(&input)?;
d.flush()?;
d.get_mut().drain(..)
}
}
.collect();
Ok(out.into())
}
pub fn op_compression_finish(
state: &mut OpState,
rid: ResourceId,
_: (),
) -> Result<ZeroCopyBuf, AnyError> {
let resource = state.resource_table.take::<CompressionResource>(rid)?;
let resource = Rc::try_unwrap(resource).unwrap();
let inner = resource.0.into_inner();
let out: Vec<u8> = match inner {
Inner::DeflateDecoder(d) => d.finish()?,
Inner::DeflateEncoder(d) => d.finish()?,
Inner::GzDecoder(d) => d.finish()?,
Inner::GzEncoder(d) => d.finish()?,
};
Ok(out.into())
}

View file

@ -809,3 +809,17 @@ declare function structuredClone(
value: any,
options?: StructuredSerializeOptions,
): any;
declare class CompressionStream {
constructor(format: string);
readonly readable: ReadableStream<Uint8Array>;
readonly writable: WritableStream<Uint8Array>;
}
declare class DecompressionStream {
constructor(format: string);
readonly readable: ReadableStream<Uint8Array>;
readonly writable: WritableStream<Uint8Array>;
}

View file

@ -1,6 +1,7 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
mod blob;
mod compression;
mod message_port;
use deno_core::error::range_error;
@ -66,6 +67,7 @@ pub fn init(blob_store: BlobStore, maybe_location: Option<Url>) -> Extension {
"11_blob_url.js",
"12_location.js",
"13_message_port.js",
"14_compression.js",
))
.ops(vec![
("op_base64_decode", op_sync(op_base64_decode)),
@ -102,6 +104,18 @@ pub fn init(blob_store: BlobStore, maybe_location: Option<Url>) -> Extension {
"op_message_port_recv_message",
op_async(op_message_port_recv_message),
),
(
"op_compression_new",
op_sync(compression::op_compression_new),
),
(
"op_compression_write",
op_sync(compression::op_compression_write),
),
(
"op_compression_finish",
op_sync(compression::op_compression_finish),
),
])
.state(move |state| {
state.put(blob_store.clone());

View file

@ -37,6 +37,7 @@ delete Object.prototype.__proto__;
const encoding = window.__bootstrap.encoding;
const colors = window.__bootstrap.colors;
const Console = window.__bootstrap.console.Console;
const compression = window.__bootstrap.compression;
const worker = window.__bootstrap.worker;
const internals = window.__bootstrap.internals;
const performance = window.__bootstrap.performance;
@ -362,11 +363,13 @@ delete Object.prototype.__proto__;
streams.ByteLengthQueuingStrategy,
),
CloseEvent: util.nonEnumerable(CloseEvent),
CompressionStream: util.nonEnumerable(compression.CompressionStream),
CountQueuingStrategy: util.nonEnumerable(
streams.CountQueuingStrategy,
),
CryptoKey: util.nonEnumerable(crypto.CryptoKey),
CustomEvent: util.nonEnumerable(CustomEvent),
DecompressionStream: util.nonEnumerable(compression.DecompressionStream),
DOMException: util.nonEnumerable(domException.DOMException),
ErrorEvent: util.nonEnumerable(ErrorEvent),
Event: util.nonEnumerable(Event),

View file

@ -4030,5 +4030,37 @@
"Pattern: [{\"pathname\":\"*/{*}\"}] Inputs: [{\"pathname\":\"foo/bar\"}]",
"Pattern: [{\"pathname\":\"*//*\"}] Inputs: [{\"pathname\":\"foo/bar\"}]"
]
},
"compression": {
"compression-bad-chunks.tentative.any.html": true,
"compression-bad-chunks.tentative.any.worker.html": true,
"compression-including-empty-chunk.tentative.any.html": true,
"compression-including-empty-chunk.tentative.any.worker.html": true,
"compression-multiple-chunks.tentative.any.html": true,
"compression-multiple-chunks.tentative.any.worker.html": true,
"compression-output-length.tentative.any.html": true,
"compression-output-length.tentative.any.worker.html": true,
"compression-stream.tentative.any.html": true,
"compression-stream.tentative.any.worker.html": true,
"compression-with-detach.tentative.window.html": true,
"decompression-bad-chunks.tentative.any.html": true,
"decompression-bad-chunks.tentative.any.worker.html": true,
"decompression-buffersource.tentative.any.html": true,
"decompression-buffersource.tentative.any.worker.html": true,
"decompression-constructor-error.tentative.any.html": true,
"decompression-constructor-error.tentative.any.worker.html": true,
"decompression-correct-input.tentative.any.html": true,
"decompression-correct-input.tentative.any.worker.html": true,
"decompression-corrupt-input.tentative.any.html": true,
"decompression-corrupt-input.tentative.any.worker.html": true,
"decompression-empty-input.tentative.any.html": true,
"decompression-empty-input.tentative.any.worker.html": true,
"decompression-split-chunk.tentative.any.html": true,
"decompression-split-chunk.tentative.any.worker.html": true,
"decompression-uint8array-output.tentative.any.html": true,
"decompression-uint8array-output.tentative.any.worker.html": true,
"decompression-with-detach.tentative.window.html": true,
"idlharness.https.any.html": true,
"idlharness.https.any.worker.html": true
}
}