Skip to content

Commit

Permalink
1180@minor: migrate fetch to ReadableStream
Browse files Browse the repository at this point in the history
  • Loading branch information
diego-toro committed Jan 8, 2024
1 parent 9a0062b commit 9c2f39a
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 61 deletions.
34 changes: 22 additions & 12 deletions packages/happy-dom/src/fetch/Fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Stream from 'stream';
import DataURIParser from './data-uri/DataURIParser.js';
import FetchCORSUtility from './utilities/FetchCORSUtility.js';
import CookieJar from '../cookie/CookieJar.js';
import { ReadableStream } from 'stream/web';

const SUPPORTED_SCHEMAS = ['data:', 'http:', 'https:'];
const REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308];
Expand Down Expand Up @@ -153,7 +154,8 @@ export default class Fetch {
const error = new DOMException('Premature close.', DOMExceptionNameEnum.networkError);

if (this.response && this.response.body) {
this.response.body.destroy(error);
const reader = this.response.body.getReader();
reader.cancel(error);
}
}
};
Expand Down Expand Up @@ -403,7 +405,7 @@ export default class Fetch {
}

const headers = new Headers(this.request.headers);
let body: Stream.Readable | Buffer | null = this.request._bodyBuffer;
let body: ReadableStream | Buffer | null = this.request._bodyBuffer;

if (!body && this.request.body) {
// Piping a used request body is not possible.
Expand All @@ -414,8 +416,19 @@ export default class Fetch {
);
}

body = new Stream.PassThrough();
this.request.body.pipe(<Stream.PassThrough>body);
body = new ReadableStream({
async start(controller) {
const bodyReader = this.request.body.getReader();
let readResult = await bodyReader.read();

while (!readResult.done) {
controller.enqueue(readResult.value);
readResult = await bodyReader.read();
}

controller.close();
}
});
}

const requestInit: IRequestInit = {
Expand Down Expand Up @@ -646,18 +659,15 @@ export default class Fetch {
const error = new DOMException('The operation was aborted.', DOMExceptionNameEnum.abortError);

if (this.request.body) {
this.request.body.destroy(error);
const reader = this.request.body.getReader();
reader.cancel(error);
}

if (!this.response || !this.response.body) {
if (this.reject) {
this.reject(error);
}
return;
if (this.response && this.response.body instanceof ReadableStream) {
const reader = this.response.body.getReader();
reader.cancel(error);
}

this.response.body.emit('error', error);

if (this.reject) {
this.reject(error);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/fetch/Request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import IRequest from './types/IRequest.js';
import Headers from './Headers.js';
import FetchBodyUtility from './utilities/FetchBodyUtility.js';
import AbortSignal from './AbortSignal.js';
import Stream from 'stream';
import { ReadableStream } from 'stream/web';
import Blob from '../file/Blob.js';
import { TextDecoder } from 'util';
import FetchRequestValidationUtility from './utilities/FetchRequestValidationUtility.js';
Expand All @@ -36,7 +36,7 @@ export default class Request implements IRequest {

// Public properties
public readonly method: string;
public readonly body: Stream.Readable | null;
public readonly body: ReadableStream | null;
public readonly headers: Headers;
public readonly redirect: IRequestRedirect;
public readonly referrerPolicy: IRequestReferrerPolicy;
Expand Down
6 changes: 3 additions & 3 deletions packages/happy-dom/src/fetch/Response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import IHeaders from './types/IHeaders.js';
import { URLSearchParams } from 'url';
import URL from '../url/URL.js';
import Blob from '../file/Blob.js';
import Stream from 'stream';
import { ReadableStream } from 'stream/web';
import FormData from '../form-data/FormData.js';
import FetchBodyUtility from './utilities/FetchBodyUtility.js';
import DOMException from '../exception/DOMException.js';
Expand All @@ -32,7 +32,7 @@ export default class Response implements IResponse {
public readonly _ownerDocument: IDocument = null;

// Public properties
public readonly body: Stream.Readable | null = null;
public readonly body: ReadableStream | null = null;
public readonly bodyUsed = false;
public readonly redirected = false;
public readonly type: 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect' =
Expand Down Expand Up @@ -250,7 +250,7 @@ export default class Response implements IResponse {
(<string>response.statusText) = this.statusText;
(<boolean>response.ok) = this.ok;
(<Headers>response.headers) = new Headers(this.headers);
(<Stream.Readable>response.body) = this.body;
(<ReadableStream>response.body) = this.body;
(<boolean>response.bodyUsed) = this.bodyUsed;
(<boolean>response.redirected) = this.redirected;
(<string>response.type) = this.type;
Expand Down
21 changes: 15 additions & 6 deletions packages/happy-dom/src/fetch/multipart/MultipartFormDataParser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import FormData from '../../form-data/FormData.js';
import Stream from 'stream';
import { ReadableStream } from 'stream/web';
import MultipartReader from './MultipartReader.js';
import DOMException from '../../exception/DOMException.js';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
Expand All @@ -19,7 +19,7 @@ export default class MultipartFormDataParser {
* @returns Form data.
*/
public static async streamToFormData(
body: Stream.Readable,
body: ReadableStream,
contentType: string
): Promise<FormData> {
if (!/multipart/i.test(contentType)) {
Expand All @@ -38,10 +38,14 @@ export default class MultipartFormDataParser {
);
}

const bodyReader = body.getReader();
const reader = new MultipartReader(match[1] || match[2]);

for await (const chunk of body) {
reader.write(chunk);
let readResult = await bodyReader.read();

while (!readResult.done) {
reader.write(readResult.value);
readResult = await bodyReader.read();
}

return reader.end();
Expand All @@ -57,7 +61,7 @@ export default class MultipartFormDataParser {
contentType: string;
contentLength: number;
buffer: Buffer;
stream: Stream.Readable;
stream: ReadableStream;
} {
const boundary = '----HappyDOMFormDataBoundary' + Math.random().toString(36);
const chunks: Buffer[] = [];
Expand Down Expand Up @@ -93,7 +97,12 @@ export default class MultipartFormDataParser {
contentType: `multipart/form-data; boundary=${boundary}`,
contentLength: buffer.length,
buffer,
stream: Stream.Readable.from(buffer)
stream: new ReadableStream({
start(controller) {
controller.enqueue(buffer);
controller.close();
}
})
};
}

Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/fetch/types/IRequest.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import IHeaders from './IHeaders.js';
import IBlob from '../../file/IBlob.js';
import AbortSignal from '../AbortSignal.js';
import Stream from 'stream';
import { ReadableStream } from 'stream/web';
import IRequestReferrerPolicy from './IRequestReferrerPolicy.js';
import IRequestRedirect from './IRequestRedirect.js';
import IRequestCredentials from './IRequestCredentials.js';
Expand All @@ -16,7 +16,7 @@ export default interface IRequest {
readonly redirect: IRequestRedirect;
readonly referrer: string;
readonly url: string;
readonly body: Stream.Readable | null;
readonly body: ReadableStream | null;
readonly bodyUsed: boolean;
readonly referrerPolicy: IRequestReferrerPolicy;
readonly signal: AbortSignal | null;
Expand Down
3 changes: 2 additions & 1 deletion packages/happy-dom/src/fetch/types/IRequestBody.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { URLSearchParams } from 'url';
import FormData from '../../form-data/FormData.js';
import Blob from '../../file/Blob.js';
import { ReadableStream } from 'stream/web';

type IRequestBody =
| ArrayBuffer
| ArrayBufferView
| NodeJS.ReadableStream
| ReadableStream
| string
| URLSearchParams
| Blob
Expand Down
4 changes: 2 additions & 2 deletions packages/happy-dom/src/fetch/types/IResponse.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import IHeaders from './IHeaders.js';
import IBlob from '../../file/IBlob.js';
import Stream from 'stream';
import { ReadableStream } from 'stream/web';

/**
* Fetch response.
Expand All @@ -15,7 +15,7 @@ export default interface IResponse {
readonly statusText: string;
readonly type: 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect';
readonly url: string;
readonly body: Stream.Readable | null;
readonly body: ReadableStream | null;
readonly bodyUsed: boolean;

arrayBuffer(): Promise<ArrayBuffer>;
Expand Down
Loading

0 comments on commit 9c2f39a

Please sign in to comment.