diff --git a/source/request-as-event-emitter.ts b/source/request-as-event-emitter.ts index acb66cfc7..b6925e4a9 100644 --- a/source/request-as-event-emitter.ts +++ b/source/request-as-event-emitter.ts @@ -1,3 +1,4 @@ +import {ReadStream} from 'fs'; import CacheableRequest = require('cacheable-request'); import EventEmitter = require('events'); import http = require('http'); @@ -15,6 +16,7 @@ import {createProgressStream} from './progress'; import timedOut, {TimeoutError as TimedOutTimeoutError} from './utils/timed-out'; import {GeneralError, NormalizedOptions, Response, requestSymbol} from './types'; import urlToOptions from './utils/url-to-options'; +import pEvent = require('p-event'); const setImmediateAsync = async (): Promise => new Promise(resolve => setImmediate(resolve)); const pipeline = promisify(stream.pipeline); @@ -301,13 +303,17 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => { }; (async () => { - // Promises are executed immediately. - // If there were no `setImmediate` here, - // `promise.json()` would have no effect - // as the request would be sent already. - await setImmediateAsync(); - try { + if (options.body instanceof ReadStream) { + await pEvent(options.body, 'open'); + } + + // Promises are executed immediately. + // If there were no `setImmediate` here, + // `promise.json()` would have no effect + // as the request would be sent already. + await setImmediateAsync(); + for (const hook of options.hooks.beforeRequest) { // eslint-disable-next-line no-await-in-loop await hook(options); diff --git a/test/post.ts b/test/post.ts index 3a423f5fd..baa689c1b 100644 --- a/test/post.ts +++ b/test/post.ts @@ -1,7 +1,10 @@ import {promisify} from 'util'; import stream = require('stream'); +import fs = require('fs'); import test from 'ava'; +import delay = require('delay'); import {Handler} from 'express'; +import getStream = require('get-stream'); import toReadableStream = require('to-readable-stream'); import got from '../source'; import withServer from './helpers/with-server'; @@ -271,3 +274,29 @@ test('DELETE method sends plain objects as JSON', withServer, async (t, server, }); t.deepEqual(body, {such: 'wow'}); }); + +test('catches body errors before calling pipeline() - promise', withServer, async (t, server, got) => { + server.post('/', defaultEndpoint); + + await t.throwsAsync(got.post({ + body: fs.createReadStream('./file-that-does-not-exist.txt') + }), { + message: /ENOENT: no such file or directory/ + }); + + // Wait for unhandled errors + await delay(100); +}); + +test('catches body errors before calling pipeline() - stream', withServer, async (t, server, got) => { + server.post('/', defaultEndpoint); + + await t.throwsAsync(getStream(got.stream.post({ + body: fs.createReadStream('./file-that-does-not-exist.txt') + })), { + message: /ENOENT: no such file or directory/ + }); + + // Wait for unhandled errors + await delay(100); +});