diff --git a/source/core/index.ts b/source/core/index.ts index b5b0525bf..81228384d 100644 --- a/source/core/index.ts +++ b/source/core/index.ts @@ -467,6 +467,12 @@ export default class Request extends Duplex implements RequestEvents { } this._request.end((error?: Error | null) => { + // The request has been destroyed before `_final` finished. + // See https://github.com/nodejs/node/issues/39356 + if ((this._request as any)._writableState?.errored) { + return; + } + if (!error) { this._bodySize = this._uploadedSize; diff --git a/test/stream.ts b/test/stream.ts index 921eac50f..d20c8ce66 100644 --- a/test/stream.ts +++ b/test/stream.ts @@ -1,7 +1,7 @@ import {promisify} from 'util'; import fs from 'fs'; import {Agent as HttpAgent} from 'http'; -import stream, {PassThrough as PassThroughStream, Readable as ReadableStream} from 'stream'; +import stream, {PassThrough as PassThroughStream, Readable as ReadableStream, Writable} from 'stream'; import {Readable as Readable2} from 'readable-stream'; import test from 'ava'; import {Handler} from 'express'; @@ -497,6 +497,20 @@ test('accepts readable-stream as body', withServer, async (t, server, got) => { t.is(response.body, 'ok'); }); +test('prevents `Cannot call end` error', async t => { + const stream = got.stream('https://example.com', { + request: () => new Writable({ + final() {} + }) as any, + timeout: { + request: 1 + } + }); + + const error: RequestError = await pEvent(stream, 'error'); + t.is(error.code, 'ETIMEDOUT'); +}); + if (Number.parseInt(process.versions.node.split('.')[0]!, 10) <= 12) { test('does not emit end event on error', withServer, async (t, server, got) => { server.get('/', infiniteHandler);