mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
refactor: make fetch use op_fetch_read instead of op_read (#7529)
This commit is contained in:
parent
1e6d37f88c
commit
46bf660e36
4 changed files with 68 additions and 28 deletions
|
@ -1,9 +1,6 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
use super::io::StreamResource;
|
|
||||||
use super::io::StreamResourceHolder;
|
|
||||||
use crate::http_util::create_http_client;
|
use crate::http_util::create_http_client;
|
||||||
use crate::http_util::HttpBody;
|
|
||||||
use deno_core::error::bad_resource_id;
|
use deno_core::error::bad_resource_id;
|
||||||
use deno_core::error::type_error;
|
use deno_core::error::type_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
@ -15,6 +12,7 @@ use http::header::HeaderName;
|
||||||
use http::header::HeaderValue;
|
use http::header::HeaderValue;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
|
use reqwest::Response;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -24,6 +22,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
pub fn init(rt: &mut deno_core::JsRuntime) {
|
pub fn init(rt: &mut deno_core::JsRuntime) {
|
||||||
super::reg_json_async(rt, "op_fetch", op_fetch);
|
super::reg_json_async(rt, "op_fetch", op_fetch);
|
||||||
|
super::reg_json_async(rt, "op_fetch_read", op_fetch_read);
|
||||||
super::reg_json_sync(rt, "op_create_http_client", op_create_http_client);
|
super::reg_json_sync(rt, "op_create_http_client", op_create_http_client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,13 +95,10 @@ async fn op_fetch(
|
||||||
res_headers.push((key.to_string(), val.to_str().unwrap().to_owned()));
|
res_headers.push((key.to_string(), val.to_str().unwrap().to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = HttpBody::from(res);
|
let rid = state
|
||||||
let rid = state.borrow_mut().resource_table.add(
|
.borrow_mut()
|
||||||
"httpBody",
|
.resource_table
|
||||||
Box::new(StreamResourceHolder::new(StreamResource::HttpBody(
|
.add("httpBody", Box::new(res));
|
||||||
Box::new(body),
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"bodyRid": rid,
|
"bodyRid": rid,
|
||||||
|
@ -112,6 +108,52 @@ async fn op_fetch(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn op_fetch_read(
|
||||||
|
state: Rc<RefCell<OpState>>,
|
||||||
|
args: Value,
|
||||||
|
_data: BufVec,
|
||||||
|
) -> Result<Value, AnyError> {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct Args {
|
||||||
|
rid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let args: Args = serde_json::from_value(args)?;
|
||||||
|
let rid = args.rid;
|
||||||
|
|
||||||
|
use futures::future::poll_fn;
|
||||||
|
use futures::ready;
|
||||||
|
use futures::FutureExt;
|
||||||
|
let f = poll_fn(move |cx| {
|
||||||
|
let mut state = state.borrow_mut();
|
||||||
|
let response = state
|
||||||
|
.resource_table
|
||||||
|
.get_mut::<Response>(rid as u32)
|
||||||
|
.ok_or_else(bad_resource_id)?;
|
||||||
|
|
||||||
|
let mut chunk_fut = response.chunk().boxed_local();
|
||||||
|
let r = ready!(chunk_fut.poll_unpin(cx))?;
|
||||||
|
if let Some(chunk) = r {
|
||||||
|
Ok(json!({ "chunk": &*chunk })).into()
|
||||||
|
} else {
|
||||||
|
Ok(json!({ "chunk": null })).into()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
f.await
|
||||||
|
/*
|
||||||
|
// I'm programming this as I want it to be programmed, even though it might be
|
||||||
|
// incorrect, normally we would use poll_fn here. We need to make this await pattern work.
|
||||||
|
let chunk = response.chunk().await?;
|
||||||
|
if let Some(chunk) = chunk {
|
||||||
|
// TODO(ry) This is terribly inefficient. Make this zero-copy.
|
||||||
|
Ok(json!({ "chunk": &*chunk }))
|
||||||
|
} else {
|
||||||
|
Ok(json!({ "chunk": null }))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
struct HttpClientResource {
|
struct HttpClientResource {
|
||||||
client: Client,
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,7 +388,7 @@
|
||||||
let { highWaterMark } = strategy;
|
let { highWaterMark } = strategy;
|
||||||
const { type } = underlyingSource;
|
const { type } = underlyingSource;
|
||||||
|
|
||||||
if (isUnderlyingByteSource(underlyingSource)) {
|
if (underlyingSource.type == "bytes") {
|
||||||
if (size !== undefined) {
|
if (size !== undefined) {
|
||||||
throw new RangeError(
|
throw new RangeError(
|
||||||
`When underlying source is "bytes", strategy.size must be undefined.`,
|
`When underlying source is "bytes", strategy.size must be undefined.`,
|
||||||
|
@ -1226,14 +1226,6 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUnderlyingByteSource(
|
|
||||||
underlyingSource,
|
|
||||||
) {
|
|
||||||
const { type } = underlyingSource;
|
|
||||||
const typeString = String(type);
|
|
||||||
return typeString === "bytes";
|
|
||||||
}
|
|
||||||
|
|
||||||
function isWritableStream(x) {
|
function isWritableStream(x) {
|
||||||
return !(
|
return !(
|
||||||
typeof x !== "object" ||
|
typeof x !== "object" ||
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
const { notImplemented } = window.__bootstrap.util;
|
const { notImplemented } = window.__bootstrap.util;
|
||||||
const { getHeaderValueParams, isTypedArray } = window.__bootstrap.webUtil;
|
const { getHeaderValueParams, isTypedArray } = window.__bootstrap.webUtil;
|
||||||
const { Blob, bytesSymbol: blobBytesSymbol } = window.__bootstrap.blob;
|
const { Blob, bytesSymbol: blobBytesSymbol } = window.__bootstrap.blob;
|
||||||
const { read } = window.__bootstrap.io;
|
|
||||||
const { close } = window.__bootstrap.resources;
|
const { close } = window.__bootstrap.resources;
|
||||||
const Body = window.__bootstrap.body;
|
const Body = window.__bootstrap.body;
|
||||||
const { ReadableStream } = window.__bootstrap.streams;
|
const { ReadableStream } = window.__bootstrap.streams;
|
||||||
|
@ -283,6 +282,7 @@
|
||||||
body,
|
body,
|
||||||
clientRid,
|
clientRid,
|
||||||
);
|
);
|
||||||
|
const rid = fetchResponse.bodyRid;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
NULL_BODY_STATUS.includes(fetchResponse.status) ||
|
NULL_BODY_STATUS.includes(fetchResponse.status) ||
|
||||||
|
@ -294,25 +294,27 @@
|
||||||
responseBody = null;
|
responseBody = null;
|
||||||
} else {
|
} else {
|
||||||
responseBody = new ReadableStream({
|
responseBody = new ReadableStream({
|
||||||
|
type: "bytes",
|
||||||
async pull(controller) {
|
async pull(controller) {
|
||||||
try {
|
try {
|
||||||
const b = new Uint8Array(1024 * 32);
|
const result = await core.jsonOpAsync("op_fetch_read", { rid });
|
||||||
const result = await read(fetchResponse.bodyRid, b);
|
if (!result || !result.chunk) {
|
||||||
if (result === null) {
|
|
||||||
controller.close();
|
controller.close();
|
||||||
return close(fetchResponse.bodyRid);
|
close(rid);
|
||||||
|
} else {
|
||||||
|
// TODO(ry) This is terribly inefficient. Make this zero-copy.
|
||||||
|
const chunk = new Uint8Array(result.chunk);
|
||||||
|
controller.enqueue(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.enqueue(b.subarray(0, result));
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
controller.error(e);
|
controller.error(e);
|
||||||
controller.close();
|
controller.close();
|
||||||
close(fetchResponse.bodyRid);
|
close(rid);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cancel() {
|
cancel() {
|
||||||
// When reader.cancel() is called
|
// When reader.cancel() is called
|
||||||
close(fetchResponse.bodyRid);
|
close(rid);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ async function startFileServer({
|
||||||
assert(s !== null && s.includes("server listening"));
|
assert(s !== null && s.includes("server listening"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
async function startFileServerAsLibrary({}: FileServerCfg = {}): Promise<void> {
|
async function startFileServerAsLibrary({}: FileServerCfg = {}): Promise<void> {
|
||||||
fileServer = await Deno.run({
|
fileServer = await Deno.run({
|
||||||
cmd: [
|
cmd: [
|
||||||
|
@ -59,6 +60,7 @@ async function startFileServerAsLibrary({}: FileServerCfg = {}): Promise<void> {
|
||||||
const s = await r.readLine();
|
const s = await r.readLine();
|
||||||
assert(s !== null && s.includes("Server running..."));
|
assert(s !== null && s.includes("Server running..."));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
async function killFileServer(): Promise<void> {
|
async function killFileServer(): Promise<void> {
|
||||||
fileServer.close();
|
fileServer.close();
|
||||||
|
@ -195,6 +197,7 @@ Deno.test("contentType", async () => {
|
||||||
(response.body as Deno.File).close();
|
(response.body as Deno.File).close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
Deno.test("file_server running as library", async function (): Promise<void> {
|
Deno.test("file_server running as library", async function (): Promise<void> {
|
||||||
await startFileServerAsLibrary();
|
await startFileServerAsLibrary();
|
||||||
try {
|
try {
|
||||||
|
@ -204,6 +207,7 @@ Deno.test("file_server running as library", async function (): Promise<void> {
|
||||||
await killFileServer();
|
await killFileServer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
async function startTlsFileServer({
|
async function startTlsFileServer({
|
||||||
target = ".",
|
target = ".",
|
||||||
|
|
Loading…
Reference in a new issue