Skip to content

Commit

Permalink
feat: data URL support in fetch (denoland#10054)
Browse files Browse the repository at this point in the history
This commit adds data URL support in `fetch`. Tested via wpt.
  • Loading branch information
lucacasonato committed Apr 10, 2021
1 parent 8d55d8b commit 3ab9498
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 37 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions op_crates/fetch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
99 changes: 62 additions & 37 deletions op_crates/fetch/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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::<FP>();
permissions.check_net_url(&url)?;
let (request_rid, request_body_rid) = match scheme {
"http" | "https" => {
let permissions = state.borrow::<FP>();
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::<std::io::Result<Vec<u8>>>(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::<std::io::Result<Vec<u8>>>(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,
Expand Down
7 changes: 7 additions & 0 deletions tools/wpt/expectation.json
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,13 @@
"Check isHistoryNavigation attribute"
]
}
},
"data-urls": {
"base64.any.js": true,
"processing.any.js": [
"\"data:https://test:test/,X\"",
"\"data:text/plain;a=\\\",\\\",X\""
]
}
},
"FileAPI": {
Expand Down

0 comments on commit 3ab9498

Please sign in to comment.