From 3ab94983b4100d5ae3c1d99c8b6a580510cd83f5 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Sat, 10 Apr 2021 23:38:15 +0200 Subject: [PATCH] feat: data URL support in fetch (#10054) This commit adds data URL support in `fetch`. Tested via wpt. --- Cargo.lock | 11 +++++ op_crates/fetch/Cargo.toml | 2 + op_crates/fetch/lib.rs | 99 ++++++++++++++++++++++++-------------- tools/wpt/expectation.json | 7 +++ 4 files changed, 82 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b359b8482f894..203c90c194d89b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -498,6 +498,15 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +[[package]] +name = "data-url" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d33fe99ccedd6e84bc035f1931bb2e6be79739d6242bd895e7311c886c50dc9c" +dependencies = [ + "matches", +] + [[package]] name = "deno" version = "1.8.3" @@ -615,7 +624,9 @@ name = "deno_fetch" version = "0.24.1" dependencies = [ "bytes", + "data-url", "deno_core", + "http", "reqwest", "serde", "tokio", diff --git a/op_crates/fetch/Cargo.toml b/op_crates/fetch/Cargo.toml index 9e86669e8e0298..b61a1ffa376951 100644 --- a/op_crates/fetch/Cargo.toml +++ b/op_crates/fetch/Cargo.toml @@ -15,7 +15,9 @@ path = "lib.rs" [dependencies] bytes = "1.0.1" +data-url = "0.1.0" deno_core = { version = "0.83.0", path = "../../core" } +http = "0.2.3" reqwest = { version = "0.11.2", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] } serde = { version = "1.0.125", features = ["derive"] } tokio = { version = "1.4.0", features = ["full"] } diff --git a/op_crates/fetch/lib.rs b/op_crates/fetch/lib.rs index 32a5a014070fbb..b3e258e5e49f55 100644 --- a/op_crates/fetch/lib.rs +++ b/op_crates/fetch/lib.rs @@ -2,6 +2,7 @@ #![deny(warnings)] +use data_url::DataUrl; use deno_core::error::bad_resource_id; use deno_core::error::generic_error; use deno_core::error::null_opbuf; @@ -157,51 +158,75 @@ where // Check scheme before asking for net permission let scheme = url.scheme(); - if scheme != "http" && scheme != "https" { - return Err(type_error(format!("scheme '{}' not supported", scheme))); - } - - let permissions = state.borrow::(); - permissions.check_net_url(&url)?; + let (request_rid, request_body_rid) = match scheme { + "http" | "https" => { + let permissions = state.borrow::(); + permissions.check_net_url(&url)?; + + let mut request = client.request(method, url); + + let request_body_rid = if args.has_body { + match data { + None => { + // If no body is passed, we return a writer for streaming the body. + let (tx, rx) = mpsc::channel::>>(1); + request = request.body(Body::wrap_stream(ReceiverStream::new(rx))); + + let request_body_rid = + state.resource_table.add(FetchRequestBodyResource { + body: AsyncRefCell::new(tx), + cancel: CancelHandle::default(), + }); + + Some(request_body_rid) + } + Some(data) => { + // If a body is passed, we use it, and don't return a body for streaming. + request = request.body(Vec::from(&*data)); + None + } + } + } else { + None + }; - let mut request = client.request(method, url); + for (key, value) in args.headers { + let name = HeaderName::from_bytes(key.as_bytes()).unwrap(); + let v = HeaderValue::from_str(&value).unwrap(); + request = request.header(name, v); + } - let request_body_rid = if args.has_body { - match data { - None => { - // If no body is passed, we return a writer for streaming the body. - let (tx, rx) = mpsc::channel::>>(1); - request = request.body(Body::wrap_stream(ReceiverStream::new(rx))); + let fut = request.send(); - let request_body_rid = - state.resource_table.add(FetchRequestBodyResource { - body: AsyncRefCell::new(tx), - cancel: CancelHandle::default(), - }); + let request_rid = state + .resource_table + .add(FetchRequestResource(Box::pin(fut))); - Some(request_body_rid) - } - Some(data) => { - // If a body is passed, we use it, and don't return a body for streaming. - request = request.body(Vec::from(&*data)); - None - } + (request_rid, request_body_rid) } - } else { - None - }; + "data" => { + let data_url = DataUrl::process(url.as_str()) + .map_err(|e| type_error(format!("{:?}", e)))?; - for (key, value) in args.headers { - let name = HeaderName::from_bytes(key.as_bytes()).unwrap(); - let v = HeaderValue::from_str(&value).unwrap(); - request = request.header(name, v); - } + let (body, _) = data_url + .decode_to_vec() + .map_err(|e| type_error(format!("{:?}", e)))?; - let fut = request.send(); + let response = http::Response::builder() + .status(http::StatusCode::OK) + .header(http::header::CONTENT_TYPE, data_url.mime_type().to_string()) + .body(reqwest::Body::from(body))?; - let request_rid = state - .resource_table - .add(FetchRequestResource(Box::pin(fut))); + let fut = async move { Ok(Response::from(response)) }; + + let request_rid = state + .resource_table + .add(FetchRequestResource(Box::pin(fut))); + + (request_rid, None) + } + _ => return Err(type_error(format!("scheme '{}' not supported", scheme))), + }; Ok(FetchReturn { request_rid, diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index 632af480a55646..a7eca6f79b6da2 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -768,6 +768,13 @@ "Check isHistoryNavigation attribute" ] } + }, + "data-urls": { + "base64.any.js": true, + "processing.any.js": [ + "\"data://test:test/,X\"", + "\"data:text/plain;a=\\\",\\\",X\"" + ] } }, "FileAPI": {