1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 21:50:00 -05:00

feat(ext/fetch): Request.bytes() and Response.bytes() (#23823)

Closes #23790
This commit is contained in:
Asher Gomez 2024-05-23 10:27:58 +10:00 committed by GitHub
parent f5d0c4b1ea
commit 8a636d0600
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 75 additions and 48 deletions

View file

@ -3156,6 +3156,8 @@ interface Body {
arrayBuffer(): Promise<ArrayBuffer>; arrayBuffer(): Promise<ArrayBuffer>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */
blob(): Promise<Blob>; blob(): Promise<Blob>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) */
bytes(): Promise<Uint8Array>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */
formData(): Promise<FormData>; formData(): Promise<FormData>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */

View file

@ -1029,6 +1029,8 @@ interface Body {
arrayBuffer(): Promise<ArrayBuffer>; arrayBuffer(): Promise<ArrayBuffer>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/blob) */
blob(): Promise<Blob>; blob(): Promise<Blob>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/bytes) */
bytes(): Promise<Uint8Array>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/formData) */
formData(): Promise<FormData>; formData(): Promise<FormData>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json) */

View file

@ -296,6 +296,15 @@ function mixinBody(prototype, bodySymbol, mimeTypeSymbol) {
configurable: true, configurable: true,
enumerable: true, enumerable: true,
}, },
bytes: {
/** @returns {Promise<Uint8Array>} */
value: function bytes() {
return consumeBody(this, "bytes");
},
writable: true,
configurable: true,
enumerable: true,
},
formData: { formData: {
/** @returns {Promise<FormData>} */ /** @returns {Promise<FormData>} */
value: function formData() { value: function formData() {
@ -330,7 +339,7 @@ function mixinBody(prototype, bodySymbol, mimeTypeSymbol) {
/** /**
* https://fetch.spec.whatwg.org/#concept-body-package-data * https://fetch.spec.whatwg.org/#concept-body-package-data
* @param {Uint8Array | string} bytes * @param {Uint8Array | string} bytes
* @param {"ArrayBuffer" | "Blob" | "FormData" | "JSON" | "text"} type * @param {"ArrayBuffer" | "Blob" | "FormData" | "JSON" | "text" | "bytes"} type
* @param {MimeType | null} [mimeType] * @param {MimeType | null} [mimeType]
*/ */
function packageData(bytes, type, mimeType) { function packageData(bytes, type, mimeType) {
@ -341,6 +350,8 @@ function packageData(bytes, type, mimeType) {
return new Blob([bytes], { return new Blob([bytes], {
type: mimeType !== null ? mimesniff.serializeMimeType(mimeType) : "", type: mimeType !== null ? mimesniff.serializeMimeType(mimeType) : "",
}); });
case "bytes":
return chunkToU8(bytes);
case "FormData": { case "FormData": {
if (mimeType !== null) { if (mimeType !== null) {
const essence = mimesniff.essence(mimeType); const essence = mimesniff.essence(mimeType);

View file

@ -58,6 +58,10 @@ declare interface Body {
* that resolves with a `Blob`. * that resolves with a `Blob`.
*/ */
blob(): Promise<Blob>; blob(): Promise<Blob>;
/** Takes a `Response` stream and reads it to completion. It returns a promise
* that resolves with a `Uint8Array`.
*/
bytes(): Promise<Uint8Array>;
/** Takes a `Response` stream and reads it to completion. It returns a promise /** Takes a `Response` stream and reads it to completion. It returns a promise
* that resolves with a `FormData` object. * that resolves with a `FormData` object.
*/ */

View file

@ -23,7 +23,7 @@ async function handler(req: Request): Promise<Response> {
method: req.method, method: req.method,
headers: headers, headers: headers,
}); });
return new Response(new Uint8Array(await resp.arrayBuffer()), { return new Response(await resp.bytes(), {
status: resp.status, status: resp.status,
headers: resp.headers, headers: resp.headers,
}); });

View file

@ -22,15 +22,11 @@ Deno.test(
.statusText, .statusText,
) )
const u8a = const u8a =
new Uint8Array( await response
await response .bytes()
.arrayBuffer(),
)
const u8a1 = const u8a1 =
new Uint8Array( await response1
await response1 .bytes()
.arrayBuffer(),
)
for ( for (
let i = 0; let i = 0;
i < i <

View file

@ -243,8 +243,8 @@ Deno.test({ permissions: { net: true } }, async function responseClone() {
assert(response !== response1); assert(response !== response1);
assertEquals(response.status, response1.status); assertEquals(response.status, response1.status);
assertEquals(response.statusText, response1.statusText); assertEquals(response.statusText, response1.statusText);
const u8a = new Uint8Array(await response.arrayBuffer()); const u8a = await response.bytes();
const u8a1 = new Uint8Array(await response1.arrayBuffer()); const u8a1 = await response1.bytes();
for (let i = 0; i < u8a.byteLength; i++) { for (let i = 0; i < u8a.byteLength; i++) {
assertEquals(u8a[i], u8a1[i]); assertEquals(u8a[i], u8a1[i]);
} }
@ -675,7 +675,7 @@ Deno.test(
["Foo", "Bar"], ["Foo", "Bar"],
], ],
}); });
await response.arrayBuffer(); await response.body?.cancel();
assertEquals(response.status, 404); assertEquals(response.status, 404);
assertEquals(response.headers.get("Content-Length"), "2"); assertEquals(response.headers.get("Content-Length"), "2");
@ -709,7 +709,7 @@ Deno.test(
["Accept-Language", "en-US"], ["Accept-Language", "en-US"],
], ],
}); });
await response.arrayBuffer(); await response.body?.cancel();
assertEquals(response.status, 404); assertEquals(response.status, 404);
assertEquals(response.headers.get("Content-Length"), "2"); assertEquals(response.headers.get("Content-Length"), "2");
@ -743,7 +743,7 @@ Deno.test(
], ],
body, body,
}); });
await response.arrayBuffer(); await response.body?.cancel();
assertEquals(response.status, 404); assertEquals(response.status, 404);
assertEquals(response.headers.get("Content-Length"), "2"); assertEquals(response.headers.get("Content-Length"), "2");
@ -782,7 +782,7 @@ Deno.test(
], ],
body, body,
}); });
await response.arrayBuffer(); await response.body?.cancel();
assertEquals(response.status, 404); assertEquals(response.status, 404);
assertEquals(response.headers.get("Content-Length"), "2"); assertEquals(response.headers.get("Content-Length"), "2");
@ -816,7 +816,7 @@ Deno.test(
["Content-Length", "10"], ["Content-Length", "10"],
], ],
}); });
await response.arrayBuffer(); await response.body?.cancel();
assertEquals(response.status, 404); assertEquals(response.status, 404);
assertEquals(response.headers.get("Content-Length"), "2"); assertEquals(response.headers.get("Content-Length"), "2");
@ -847,7 +847,7 @@ Deno.test(
["Transfer-Encoding", "chunked"], ["Transfer-Encoding", "chunked"],
], ],
}); });
await response.arrayBuffer(); await response.body?.cancel();
assertEquals(response.status, 404); assertEquals(response.status, 404);
assertEquals(response.headers.get("Content-Length"), "2"); assertEquals(response.headers.get("Content-Length"), "2");
@ -933,7 +933,7 @@ Deno.test(function responseRedirectTakeURLObjectAsParameter() {
Deno.test(async function responseWithoutBody() { Deno.test(async function responseWithoutBody() {
const response = new Response(); const response = new Response();
assertEquals(await response.arrayBuffer(), new ArrayBuffer(0)); assertEquals(await response.bytes(), new Uint8Array(0));
const blob = await response.blob(); const blob = await response.blob();
assertEquals(blob.size, 0); assertEquals(blob.size, 0);
assertEquals(await blob.arrayBuffer(), new ArrayBuffer(0)); assertEquals(await blob.arrayBuffer(), new ArrayBuffer(0));
@ -1214,7 +1214,7 @@ Deno.test(
], ],
body: stream.readable, body: stream.readable,
}); });
await response.arrayBuffer(); await response.body?.cancel();
assertEquals(response.status, 404); assertEquals(response.status, 404);
assertEquals(response.headers.get("Content-Length"), "2"); assertEquals(response.headers.get("Content-Length"), "2");
@ -1793,10 +1793,9 @@ Deno.test(
const listener = invalidServer(addr, body); const listener = invalidServer(addr, body);
const response = await fetch(`http://${addr}/`); const response = await fetch(`http://${addr}/`);
const res = await response.arrayBuffer(); const res = await response.bytes();
const buf = new TextEncoder().encode(data); const buf = new TextEncoder().encode(data);
assertEquals(res.byteLength, buf.byteLength); assertEquals(res, buf);
assertEquals(new Uint8Array(res), buf);
listener.close(); listener.close();
}, },
@ -1850,10 +1849,10 @@ Deno.test(
// If content-length < totalLength, a maximum of content-length bytes // If content-length < totalLength, a maximum of content-length bytes
// should be returned. // should be returned.
const res = await response.arrayBuffer(); const res = await response.bytes();
const buf = new TextEncoder().encode(data); const buf = new TextEncoder().encode(data);
assertEquals(res.byteLength, contentLength); assertEquals(res.byteLength, contentLength);
assertEquals(new Uint8Array(res), buf.subarray(contentLength)); assertEquals(res, buf.subarray(contentLength));
listener.close(); listener.close();
}, },
@ -2029,7 +2028,7 @@ Deno.test(
Deno.test("Request with subarray TypedArray body", async () => { Deno.test("Request with subarray TypedArray body", async () => {
const body = new Uint8Array([1, 2, 3, 4, 5]).subarray(1); const body = new Uint8Array([1, 2, 3, 4, 5]).subarray(1);
const req = new Request("https://example.com", { method: "POST", body }); const req = new Request("https://example.com", { method: "POST", body });
const actual = new Uint8Array(await req.arrayBuffer()); const actual = await req.bytes();
const expected = new Uint8Array([2, 3, 4, 5]); const expected = new Uint8Array([2, 3, 4, 5]);
assertEquals(actual, expected); assertEquals(actual, expected);
}); });
@ -2037,7 +2036,7 @@ Deno.test("Request with subarray TypedArray body", async () => {
Deno.test("Response with subarray TypedArray body", async () => { Deno.test("Response with subarray TypedArray body", async () => {
const body = new Uint8Array([1, 2, 3, 4, 5]).subarray(1); const body = new Uint8Array([1, 2, 3, 4, 5]).subarray(1);
const req = new Response(body); const req = new Response(body);
const actual = new Uint8Array(await req.arrayBuffer()); const actual = await req.bytes();
const expected = new Uint8Array([2, 3, 4, 5]); const expected = new Uint8Array([2, 3, 4, 5]);
assertEquals(actual, expected); assertEquals(actual, expected);
}); });

View file

@ -239,7 +239,7 @@ Deno.test(
headers: { "connection": "close" }, headers: { "connection": "close" },
}); });
await resp.arrayBuffer(); await resp.body?.cancel();
await promise; await promise;
}, },
); );
@ -963,7 +963,7 @@ Deno.test(
await respondWith(new Response(f.readable, { status: 200 })); await respondWith(new Response(f.readable, { status: 200 }));
})(); })();
const resp = await fetch(`http://127.0.0.1:${listenPort}/`); const resp = await fetch(`http://127.0.0.1:${listenPort}/`);
const body = await resp.arrayBuffer(); const body = await resp.bytes();
assertEquals(body.byteLength, 70 * 1024); assertEquals(body.byteLength, 70 * 1024);
await promise; await promise;
httpConn!.close(); httpConn!.close();
@ -1293,8 +1293,8 @@ Deno.test(
const resp = await fetch(`http://localhost:${listenPort}/`); const resp = await fetch(`http://localhost:${listenPort}/`);
assertEquals(resp.status, 200); assertEquals(resp.status, 200);
const body = await resp.arrayBuffer(); const body = await resp.bytes();
assertEquals(new Uint8Array(body), new Uint8Array([128])); assertEquals(body, new Uint8Array([128]));
await promise; await promise;
httpConn!.close(); httpConn!.close();

View file

@ -340,7 +340,7 @@ Deno.test(
}); });
const resp = await fetch(`http://localhost:${servePort}`); const resp = await fetch(`http://localhost:${servePort}`);
dataPromise = resp.arrayBuffer(); dataPromise = resp.bytes();
} }
assertEquals((await dataPromise).byteLength, 1048576); assertEquals((await dataPromise).byteLength, 1048576);
@ -358,7 +358,7 @@ Deno.test(
const [_, data] = await Promise.all([ const [_, data] = await Promise.all([
server.shutdown(), server.shutdown(),
resp.arrayBuffer(), resp.bytes(),
]); ]);
assertEquals(data.byteLength, 1048576); assertEquals(data.byteLength, 1048576);
@ -1861,13 +1861,12 @@ Deno.test(
signal: ac.signal, signal: ac.signal,
}); });
const response = await fetch(`http://localhost:${servePort}/`); const response = await fetch(`http://localhost:${servePort}/`);
const body = await response.arrayBuffer(); const body = await response.bytes();
assertEquals(1024 * 1024, body.byteLength); assertEquals(1024 * 1024, body.byteLength);
const buffer = new Uint8Array(body);
for (let i = 0; i < 256; i++) { for (let i = 0; i < 256; i++) {
assertEquals( assertEquals(
i, i,
buffer[i * 4096], body[i * 4096],
`sentinel mismatch at index ${i * 4096}`, `sentinel mismatch at index ${i * 4096}`,
); );
} }
@ -2078,8 +2077,8 @@ Deno.test(
await deferred.promise; await deferred.promise;
assertEquals(resp.status, 200); assertEquals(resp.status, 200);
const body = await resp.arrayBuffer(); const body = await resp.bytes();
assertEquals(new Uint8Array(body), new Uint8Array([128])); assertEquals(body, new Uint8Array([128]));
ac.abort(); ac.abort();
await server.finished; await server.finished;
@ -2694,7 +2693,7 @@ for (const testCase of compressionTestCases) {
headers: testCase.in as HeadersInit, headers: testCase.in as HeadersInit,
}); });
await deferred.promise; await deferred.promise;
const body = await resp.arrayBuffer(); const body = await resp.bytes();
if (testCase.expect == null) { if (testCase.expect == null) {
assertEquals(body.byteLength, testCase.length); assertEquals(body.byteLength, testCase.length);
assertEquals( assertEquals(
@ -2731,7 +2730,7 @@ Deno.test(
const server = Deno.serve({ const server = Deno.serve({
handler: async (request) => { handler: async (request) => {
assertEquals( assertEquals(
new Uint8Array(await request.arrayBuffer()), await request.bytes(),
makeTempData(70 * 1024), makeTempData(70 * 1024),
); );
deferred.resolve(); deferred.resolve();

View file

@ -689,7 +689,7 @@ Deno.test({
assert(worker); assert(worker);
const response = await fetch("http://localhost:4506"); const response = await fetch("http://localhost:4506");
assert(await response.arrayBuffer()); assert(await response.bytes());
worker.terminate(); worker.terminate();
}, },
}); });

View file

@ -174,7 +174,7 @@ Deno.test("[node/http] server can respond with 101, 204, 205, 304 status", async
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
`http://127.0.0.1:${(server.address() as any).port}/`, `http://127.0.0.1:${(server.address() as any).port}/`,
); );
await res.arrayBuffer(); await res.body?.cancel();
assertEquals(res.status, status); assertEquals(res.status, status);
server.close(() => resolve()); server.close(() => resolve());
}); });

View file

@ -7153,6 +7153,12 @@
"response": { "response": {
"json.any.html": true, "json.any.html": true,
"json.any.worker.html": true, "json.any.worker.html": true,
"response-blob-realm.any.html": [
"realm of the Uint8Array from Response bytes()"
],
"response-blob-realm.any.worker.html": [
"realm of the Uint8Array from Response bytes()"
],
"response-init-001.any.html": true, "response-init-001.any.html": true,
"response-init-001.any.worker.html": true, "response-init-001.any.worker.html": true,
"response-init-002.any.html": true, "response-init-002.any.html": true,
@ -8148,7 +8154,11 @@
"HTTP/1.1 200 ", "HTTP/1.1 200 ",
"HTTP/1.1 999 DOES IT MATTER " "HTTP/1.1 999 DOES IT MATTER "
], ],
"resources-with-0x00-in-header.window.html": false "resources-with-0x00-in-header.window.html": [
"Expect network error for script with 0x00 in a header",
"Expect network error for frame navigation to resource with 0x00 in a header",
"Expect network error for image with 0x00 in a header"
]
}, },
"range": { "range": {
"general.any.html": [ "general.any.html": [
@ -12825,8 +12835,6 @@
"eventsource-onopen.any.worker.html": true, "eventsource-onopen.any.worker.html": true,
"eventsource-prototype.any.html": true, "eventsource-prototype.any.html": true,
"eventsource-prototype.any.worker.html": true, "eventsource-prototype.any.worker.html": true,
"eventsource-request-cancellation.window.any.html": false,
"eventsource-request-cancellation.window.any.worker.html": false,
"eventsource-url.any.html": true, "eventsource-url.any.html": true,
"eventsource-url.any.worker.html": true, "eventsource-url.any.worker.html": true,
"format-bom-2.any.html": true, "format-bom-2.any.html": true,
@ -12883,6 +12891,12 @@
"eventsource-constructor-stringify.window.html": false, "eventsource-constructor-stringify.window.html": false,
"eventsource-cross-origin.window.html": false, "eventsource-cross-origin.window.html": false,
"eventsource-reconnect.window.html": false, "eventsource-reconnect.window.html": false,
"request-status-error.window.html": false "request-status-error.window.html": false,
"eventsource-constructor-empty-url.any.serviceworker.html": false,
"eventsource-constructor-empty-url.any.sharedworker.html": false,
"eventsource-constructor-url-bogus.any.serviceworker.html": false,
"eventsource-constructor-url-bogus.any.sharedworker.html": false,
"request-credentials.window.html": false,
"request-redirect.window.html": false
} }
} }

@ -1 +1 @@
Subproject commit 5e8f71d73049d4fca2a8cbc62d40e821400f1624 Subproject commit 915d40b37fbd3554548d5cbec9f335f329ccc944