diff --git a/package-lock.json b/package-lock.json index d4b19779..61b4ebab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,8 @@ "requires": true, "packages": { "": { - "version": "1.0.2", + "name": "react-native-fetch-api", + "version": "3.0.0", "license": "MIT", "dependencies": { "p-defer": "^3.0.0" diff --git a/src/Body.js b/src/Body.js index bcc594e8..60f314ad 100644 --- a/src/Body.js +++ b/src/Body.js @@ -6,6 +6,7 @@ class Body { this._bodyInit = body; if (!body) { + this._bodyNull = true; this._bodyText = ""; return this; } @@ -179,6 +180,10 @@ class Body { } get body() { + if (this._bodyNull) { + return null; + } + if (this._bodyReadableStream) { return this._bodyReadableStream; } @@ -228,6 +233,16 @@ class Body { }, }); } + + clone() { + if (this._bodyReadableStream) { + const [stream1, stream2] = this._bodyReadableStream.tee(); + this._bodyReadableStream = stream1; + return new Body(stream2); + } else { + return new Body(this._bodyInit); + } + } } export default Body; diff --git a/src/Request.js b/src/Request.js index ab814624..68ed5690 100644 --- a/src/Request.js +++ b/src/Request.js @@ -46,7 +46,7 @@ class Request { this.signal = request.signal; this.headers = new Headers(options.headers ?? request.headers); - if (!options.body && request._body._bodyInit) { + if (options.body === undefined && request._body._bodyInit) { this._body = new Body(request._body._bodyInit); request._body.bodyUsed = true; } @@ -85,7 +85,14 @@ class Request { } clone() { - return new Request(this, { body: this._body._bodyInit }); + if (this.bodyUsed) { + throw new TypeError("Already read"); + } + + const newRequest = new Request(this, { body: null }); + newRequest._body = this._body.clone(); + + return newRequest; } blob() { diff --git a/src/Response.js b/src/Response.js index 446e237b..353dc6a8 100644 --- a/src/Response.js +++ b/src/Response.js @@ -21,12 +21,19 @@ class Response { } clone() { - return new Response(this._body._bodyInit, { + if (this.bodyUsed) { + throw new TypeError("Already read"); + } + + const newResponse = new Response(null, { status: this.status, statusText: this.statusText, headers: new Headers(this.headers), url: this.url, }); + newResponse._body = this._body.clone(); + + return newResponse; } blob() { diff --git a/test/index.js b/test/index.js index da1751e2..d37e957e 100644 --- a/test/index.js +++ b/test/index.js @@ -553,7 +553,7 @@ test("request", (t) => { t.isNot(clone.headers, req.headers); t.notOk(req.bodyUsed); - const bodies = await Promise.all([clone.text(), req.clone().text()]); + const bodies = await Promise.all([clone.text(), req.text()]); t.eq(bodies, ["I work out", "I work out"]); }); @@ -1864,14 +1864,27 @@ test("fetch method", (t) => { reactNative: { textStreaming: true }, }); const clone = res.clone(); - const stream = await clone.body; - const text = new TextDecoder().decode(await drainStream(stream)); + + const resStream = await res.body; + const cloneStream = await clone.body; + const resText = new TextDecoder().decode( + await drainStream(resStream) + ); + const cloneText = new TextDecoder().decode( + await drainStream(cloneStream) + ); t.ok( - stream instanceof ReadableStream, + resStream instanceof ReadableStream, "Response implements streaming body" ); - t.eq(text, "Hello world!"); + t.eq(resText, "Hello world!"); + + t.ok( + cloneStream instanceof ReadableStream, + "Response implements streaming body" + ); + t.eq(cloneText, "Hello world!"); }); t.test("cloning blob response", async (t) => { @@ -1882,8 +1895,11 @@ test("fetch method", (t) => { }, }); const clone = res.clone(); - const json = await clone.json(); - t.eq(json.headers.accept, "application/json"); + const resJson = await res.json(); + const cloneJson = await clone.json(); + + t.eq(resJson.headers.accept, "application/json"); + t.eq(cloneJson.headers.accept, "application/json"); }); t.test("cloning array buffer response", async (t) => { @@ -1894,15 +1910,28 @@ test("fetch method", (t) => { }, }); const clone = res.clone(); - const buf = await clone.arrayBuffer(); + const resBuf = await res.arrayBuffer(); + const cloneBuf = await clone.arrayBuffer(); - t.ok(buf instanceof ArrayBuffer, "buf is an ArrayBuffer instance"); - t.eq(buf.byteLength, 256, "buf.byteLength is correct"); + t.ok( + resBuf instanceof ArrayBuffer, + "buf is an ArrayBuffer instance" + ); + t.eq(resBuf.byteLength, 256, "buf.byteLength is correct"); + + t.ok( + cloneBuf instanceof ArrayBuffer, + "buf is an ArrayBuffer instance" + ); + t.eq(cloneBuf.byteLength, 256, "buf.byteLength is correct"); const expected = Array.from({ length: 256 }, (_, i) => i); - const actual = Array.from(new Uint8Array(buf)); - t.eq(actual, expected); + const resActual = Array.from(new Uint8Array(resBuf)); + const cloneActual = Array.from(new Uint8Array(cloneBuf)); + + t.eq(resActual, expected); + t.eq(cloneActual, expected); }); }); });