Skip to content

Commit

Permalink
Fix unhandled Premature close errors
Browse files Browse the repository at this point in the history
Fixes #990
  • Loading branch information
szmarczak committed Dec 19, 2019
1 parent db51652 commit fa60b5f
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 22 deletions.
10 changes: 7 additions & 3 deletions source/get-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export default async (response: IncomingMessage, options: NormalizedOptions, emi
const newResponse = (
options.decompress &&
options.method !== 'HEAD' ? decompressResponse(progressStream as unknown as IncomingMessage) : progressStream
);
) as IncomingMessage;

if (!options.decompress && ['gzip', 'deflate', 'br'].includes(response.headers['content-encoding'] ?? '')) {
if (!options.decompress && ['gzip', 'deflate', 'br'].includes(newResponse.headers['content-encoding'] ?? '')) {
options.responseType = 'buffer';
}

Expand All @@ -29,5 +29,9 @@ export default async (response: IncomingMessage, options: NormalizedOptions, emi
return pipeline(
response,
progressStream
);
).catch(error => {
if (error.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
throw error;
}
});
};
30 changes: 15 additions & 15 deletions source/request-as-event-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import getResponse from './get-response';
import {normalizeRequestArguments} from './normalize-arguments';
import {createProgressStream} from './progress';
import timedOut, {TimeoutError as TimedOutTimeoutError} from './utils/timed-out';
import {GeneralError, NormalizedOptions, Response, ResponseObject} from './utils/types';
import {GeneralError, NormalizedOptions, Response} from './utils/types';
import urlToOptions from './utils/url-to-options';

const setImmediateAsync = async (): Promise<void> => new Promise(resolve => setImmediate(resolve));
Expand Down Expand Up @@ -54,7 +54,7 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
const get = async (): Promise<void> => {
let httpOptions = await normalizeRequestArguments(options);

const handleResponse = async (response: http.ServerResponse | ResponseObject): Promise<void> => {
const handleResponse = async (response: http.IncomingMessage): Promise<void> => {
try {
/* istanbul ignore next: fixes https://github.com/electron/electron/blob/cbb460d47628a7a146adf4419ed48550a98b2923/lib/browser/api/net.js#L59-L65 */
if (options.useElectronNet) {
Expand Down Expand Up @@ -107,9 +107,17 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
options.method = 'GET';
}

delete options.body;
delete options.json;
delete options.form;
if (Reflect.has(options, 'body')) {
delete options.body;
}

if (Reflect.has(options, 'json')) {
delete options.json;
}

if (Reflect.has(options, 'form')) {
delete options.form;
}
}

if (redirects.length >= options.maxRedirects) {
Expand All @@ -121,7 +129,7 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
const redirectURL = new URL(redirectBuffer, options.url);

// Redirecting to a different site, clear cookies.
if (redirectURL.hostname !== options.url.hostname) {
if (redirectURL.hostname !== options.url.hostname && Reflect.has(options.headers, 'cookie')) {
delete options.headers.cookie;
}

Expand All @@ -139,14 +147,7 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
return;
}

try {
await getResponse(typedResponse, options, emitter);
} catch (error) {
// Don't throw `Premature close` if the request has been aborted
if (!(isAborted() && error.message === 'Premature close')) {
throw error;
}
}
await getResponse(typedResponse, options, emitter);
} catch (error) {
emitError(error);
}
Expand Down Expand Up @@ -240,7 +241,6 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
} else {
// Catches errors thrown by calling `requestFn(…)`
try {
// @ts-ignore ResponseObject does not equal IncomingMessage
handleRequest(httpOptions.request(options.url, httpOptions, handleResponse));
} catch (error) {
emitError(new RequestError(error, options));
Expand Down
5 changes: 4 additions & 1 deletion test/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ test('errors are thrown directly when options.stream is true', t => {
});
});

test('the old stacktrace is recovered', async t => {
// Fails randomly on Node 10:
// Blocked by https://github.com/istanbuljs/nyc/issues/619
// eslint-disable-next-line ava/no-skip-test
test.skip('the old stacktrace is recovered', async t => {
const error = await t.throwsAsync(got('https://example.com', {
request: () => {
throw new Error('foobar');
Expand Down
12 changes: 12 additions & 0 deletions test/gzip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ test('handles gzip error', withServer, async (t, server, got) => {
});
});

test('no unhandled `Premature close` error', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.setHeader('Content-Encoding', 'gzip');
response.write('Not gzipped content');
});

await t.throwsAsync(got(''), {
name: 'ReadError',
message: 'incorrect header check'
});
});

test('handles gzip error - stream', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.setHeader('Content-Encoding', 'gzip');
Expand Down
14 changes: 11 additions & 3 deletions test/options-to-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,17 @@ test('`path` and `searchParams` are mutually exclusive', t => {
});

test('`path` option', t => {
const url = optionsToUrl({origin, path: '/?a=1'});
t.is(url.href, `${origin}/?a=1`);
t.true(is.urlInstance(url));
{
const url = optionsToUrl({origin, path: '/x?a=1'});
t.is(url.href, `${origin}/x?a=1`);
t.true(is.urlInstance(url));
}

{
const url = optionsToUrl({origin, path: '/foobar'});
t.is(url.href, `${origin}/foobar`);
t.true(is.urlInstance(url));
}
});

test('`auth` is deprecated', t => {
Expand Down
22 changes: 22 additions & 0 deletions test/redirects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,28 @@ test('body is reset on GET redirect', withServer, async (t, server, got) => {
]
}
});

await got.post('', {
json: {foo: 'bar'},
hooks: {
beforeRedirect: [
options => {
t.is(options.body, undefined);
}
]
}
});

await got.post('', {
form: {foo: 'bar'},
hooks: {
beforeRedirect: [
options => {
t.is(options.body, undefined);
}
]
}
});
});

test('body is passed on POST redirect', withServer, async (t, server, got) => {
Expand Down

0 comments on commit fa60b5f

Please sign in to comment.