Skip to content

Commit dc4f1e3

Browse files
committed
Fix hang with stream requests without body for methods like OPTIONS
Fixes #2394 When using got.stream() with methods like OPTIONS, DELETE, or PATCH without a body, the request would hang indefinitely. This was because the condition to end the request only checked _cannotHaveBody (GET/HEAD) or _noPipe, missing the case where a stream request has no body to send.
1 parent 75287d6 commit dc4f1e3

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

source/core/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ export default class Request extends Duplex implements RequestEvents<Request> {
168168
private _bodySize?: number;
169169
private _unproxyEvents: () => void;
170170
private _isFromCache?: boolean;
171-
private _cannotHaveBody: boolean;
172171
private _triggerRead: boolean;
173172
declare private readonly _jobs: Array<() => void>;
174173
private _cancelTimeouts: () => void;
@@ -192,7 +191,6 @@ export default class Request extends Duplex implements RequestEvents<Request> {
192191
this._uploadedSize = 0;
193192
this._stopReading = false;
194193
this._pipedServerResponses = new Set<ServerResponse>();
195-
this._cannotHaveBody = false;
196194
this._unproxyEvents = noop;
197195
this._triggerRead = false;
198196
this._cancelTimeouts = noop;
@@ -567,8 +565,6 @@ export default class Request extends Duplex implements RequestEvents<Request> {
567565
const isBody = !is.undefined(options.body);
568566
const cannotHaveBody = methodsWithoutBody.has(options.method) && !(options.method === 'GET' && options.allowGetBody);
569567

570-
this._cannotHaveBody = cannotHaveBody;
571-
572568
if (isForm || isJSON || isBody) {
573569
if (cannotHaveBody) {
574570
throw new TypeError(`The \`${options.method}\` method cannot be used with a body`);
@@ -992,11 +988,15 @@ export default class Request extends Duplex implements RequestEvents<Request> {
992988
this._beforeError(error);
993989
}
994990
})();
995-
} else if (!is.undefined(body)) {
991+
} else if (is.undefined(body)) {
992+
// No body to send, end the request
993+
const cannotHaveBody = methodsWithoutBody.has(this.options.method) && !(this.options.method === 'GET' && this.options.allowGetBody);
994+
if ((this._noPipe ?? false) || cannotHaveBody || currentRequest !== this) {
995+
currentRequest.end();
996+
}
997+
} else {
996998
this._writeRequest(body, undefined, () => {});
997999
currentRequest.end();
998-
} else if (this._cannotHaveBody || this._noPipe) {
999-
currentRequest.end();
10001000
}
10011001
}
10021002

test/stream.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,3 +501,36 @@ testFunction('it sends a body of file with size on stat = 0', withServer, async
501501

502502
t.truthy(response.body);
503503
});
504+
505+
test('OPTIONS stream without body completes successfully', withServer, async (t, server, got) => {
506+
server.options('/', (_request, response) => {
507+
response.writeHead(200, {
508+
allow: 'OPTIONS, GET, HEAD, POST',
509+
});
510+
response.end();
511+
});
512+
513+
const stream = got.stream({method: 'OPTIONS'});
514+
await t.notThrowsAsync(getStream(stream));
515+
});
516+
517+
test('DELETE stream without body completes successfully', withServer, async (t, server, got) => {
518+
server.delete('/', (_request, response) => {
519+
response.writeHead(204);
520+
response.end();
521+
});
522+
523+
const stream = got.stream({method: 'DELETE'});
524+
await t.notThrowsAsync(getStream(stream));
525+
});
526+
527+
test('PATCH stream without body completes successfully', withServer, async (t, server, got) => {
528+
server.patch('/', (_request, response) => {
529+
response.writeHead(200);
530+
response.end('patched');
531+
});
532+
533+
const stream = got.stream({method: 'PATCH'});
534+
const data = await getStream(stream);
535+
t.is(data, 'patched');
536+
});

0 commit comments

Comments
 (0)