Skip to content

Commit bb8eca9

Browse files
committed
Remove stream lock - unreliable since Node 17.3.0
1 parent e393662 commit bb8eca9

File tree

3 files changed

+7
-250
lines changed

3 files changed

+7
-250
lines changed

source/core/index.ts

Lines changed: 7 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import process from 'node:process';
22
import {Buffer} from 'node:buffer';
3-
import {Duplex, Writable, Readable} from 'node:stream';
3+
import {Duplex, Readable} from 'node:stream';
44
import {URL, URLSearchParams} from 'node:url';
55
import http, {ServerResponse} from 'node:http';
66
import type {ClientRequest, RequestOptions} from 'node:http';
@@ -197,30 +197,6 @@ export default class Request extends Duplex implements RequestEvents<Request> {
197197

198198
this._stopRetry = noop;
199199

200-
const unlockWrite = (): void => {
201-
this._unlockWrite();
202-
};
203-
204-
const lockWrite = (): void => {
205-
this._lockWrite();
206-
};
207-
208-
this.on('pipe', (source: Writable) => {
209-
source.prependListener('data', unlockWrite);
210-
source.on('data', lockWrite);
211-
212-
source.prependListener('end', unlockWrite);
213-
source.on('end', lockWrite);
214-
});
215-
216-
this.on('unpipe', (source: Writable) => {
217-
source.off('data', unlockWrite);
218-
source.off('data', lockWrite);
219-
220-
source.off('end', unlockWrite);
221-
source.off('end', lockWrite);
222-
});
223-
224200
this.on('pipe', source => {
225201
if (source.headers) {
226202
Object.assign(this.options.headers, source.headers);
@@ -259,13 +235,9 @@ export default class Request extends Duplex implements RequestEvents<Request> {
259235
return;
260236
}
261237

262-
const {json, body, form} = this.options;
263-
if (json || body || form) {
264-
this._lockWrite();
265-
}
266-
267238
// Important! If you replace `body` in a handler with another stream, make sure it's readable first.
268239
// The below is run only once.
240+
const {body} = this.options;
269241
if (is.nodeStream(body)) {
270242
body.once('error', error => {
271243
if (this._flushed) {
@@ -553,20 +525,6 @@ export default class Request extends Duplex implements RequestEvents<Request> {
553525
return this;
554526
}
555527

556-
private _lockWrite(): void {
557-
const onLockedWrite = (): never => {
558-
throw new TypeError('The payload has been already provided');
559-
};
560-
561-
this.write = onLockedWrite;
562-
this.end = onLockedWrite;
563-
}
564-
565-
private _unlockWrite(): void {
566-
this.write = super.write;
567-
this.end = super.end;
568-
}
569-
570528
private async _finalizeBody(): Promise<void> {
571529
const {options} = this;
572530
const {headers} = options;
@@ -639,10 +597,6 @@ export default class Request extends Duplex implements RequestEvents<Request> {
639597
if (is.undefined(headers['content-length']) && is.undefined(headers['transfer-encoding']) && !cannotHaveBody && !is.undefined(uploadBodySize)) {
640598
headers['content-length'] = String(uploadBodySize);
641599
}
642-
} else if (cannotHaveBody) {
643-
this._lockWrite();
644-
} else {
645-
this._unlockWrite();
646600
}
647601

648602
if (options.responseType === 'json' && !('accept' in options.headers)) {
@@ -974,19 +928,11 @@ export default class Request extends Duplex implements RequestEvents<Request> {
974928
this._beforeError(error);
975929
}
976930
})();
977-
} else {
978-
this._unlockWrite();
979-
980-
if (!is.undefined(body)) {
981-
this._writeRequest(body, undefined, () => {});
982-
currentRequest.end();
983-
984-
this._lockWrite();
985-
} else if (this._cannotHaveBody || this._noPipe) {
986-
currentRequest.end();
987-
988-
this._lockWrite();
989-
}
931+
} else if (!is.undefined(body)) {
932+
this._writeRequest(body, undefined, () => {});
933+
currentRequest.end();
934+
} else if (this._cannotHaveBody || this._noPipe) {
935+
currentRequest.end();
990936
}
991937
}
992938

test/progress.ts

Lines changed: 0 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import {Buffer} from 'buffer';
2-
import {promisify} from 'util';
32
import stream from 'stream';
4-
import fs from 'fs';
53
// @ts-expect-error Fails to find slow-stream/index.d.ts
64
import SlowStream from 'slow-stream';
75
import toReadableStream from 'to-readable-stream';
86
import getStream from 'get-stream';
9-
import FormData from 'form-data';
10-
import tempy from 'tempy';
117
import is from '@sindresorhus/is';
128
import test, {ExecutionContext} from 'ava';
139
import {Handler} from 'express';
@@ -45,24 +41,6 @@ const checkEvents = (t: ExecutionContext, events: Progress[], bodySize?: number)
4541

4642
const file = Buffer.alloc(1024 * 1024 * 2);
4743

48-
const downloadEndpoint: Handler = (_request, response) => {
49-
response.setHeader('content-length', file.length);
50-
51-
stream.pipeline(
52-
toReadableStream(file),
53-
new SlowStream({maxWriteInterval: 50}),
54-
response,
55-
() => {
56-
response.end();
57-
},
58-
);
59-
};
60-
61-
const noTotalEndpoint: Handler = (_request, response) => {
62-
response.write('hello');
63-
response.end();
64-
};
65-
6644
const uploadEndpoint: Handler = (request, response) => {
6745
stream.pipeline(
6846
request,
@@ -73,118 +51,6 @@ const uploadEndpoint: Handler = (request, response) => {
7351
);
7452
};
7553

76-
test('download progress', withServer, async (t, server, got) => {
77-
server.get('/', downloadEndpoint);
78-
79-
const events: Progress[] = [];
80-
81-
const {body} = await got({responseType: 'buffer'})
82-
.on('downloadProgress', event => events.push(event));
83-
84-
checkEvents(t, events, body.length);
85-
});
86-
87-
test('download progress - missing total size', withServer, async (t, server, got) => {
88-
server.get('/', noTotalEndpoint);
89-
90-
const events: Progress[] = [];
91-
92-
await got('').on('downloadProgress', (event: Progress) => events.push(event));
93-
94-
t.is(events[0]?.total, undefined);
95-
checkEvents(t, events);
96-
});
97-
98-
test('download progress - stream', withServer, async (t, server, got) => {
99-
server.get('/', downloadEndpoint);
100-
101-
const events: Progress[] = [];
102-
103-
const stream = got.stream({responseType: 'buffer'})
104-
.on('downloadProgress', event => events.push(event));
105-
106-
await getStream(stream);
107-
108-
checkEvents(t, events, file.length);
109-
});
110-
111-
test('upload progress - file', withServer, async (t, server, got) => {
112-
server.post('/', uploadEndpoint);
113-
114-
const events: Progress[] = [];
115-
116-
await got.post({body: file}).on('uploadProgress', (event: Progress) => events.push(event));
117-
118-
checkEvents(t, events, file.length);
119-
});
120-
121-
test('upload progress - file stream', withServer, async (t, server, got) => {
122-
server.post('/', uploadEndpoint);
123-
124-
const path = tempy.file();
125-
fs.writeFileSync(path, file);
126-
127-
const {size} = await promisify(fs.stat)(path);
128-
129-
const events: Progress[] = [];
130-
131-
await got.post({
132-
body: fs.createReadStream(path),
133-
headers: {
134-
'content-length': size.toString(),
135-
},
136-
})
137-
.on('uploadProgress', (event: Progress) => events.push(event));
138-
139-
checkEvents(t, events, file.length);
140-
});
141-
142-
test('upload progress - form data', withServer, async (t, server, got) => {
143-
server.post('/', uploadEndpoint);
144-
145-
const events: Progress[] = [];
146-
147-
const body = new FormData();
148-
body.append('key', 'value');
149-
body.append('file', file);
150-
151-
const size = await promisify(body.getLength.bind(body))();
152-
153-
await got.post({body}).on('uploadProgress', (event: Progress) => events.push(event));
154-
155-
checkEvents(t, events, size);
156-
});
157-
158-
test('upload progress - json', withServer, async (t, server, got) => {
159-
server.post('/', uploadEndpoint);
160-
161-
const body = JSON.stringify({key: 'value'});
162-
const size = Buffer.byteLength(body);
163-
const events: Progress[] = [];
164-
165-
await got.post({body}).on('uploadProgress', (event: Progress) => events.push(event));
166-
167-
checkEvents(t, events, size);
168-
});
169-
170-
test('upload progress - stream with known body size', withServer, async (t, server, got) => {
171-
server.post('/', uploadEndpoint);
172-
173-
const events: Progress[] = [];
174-
const options = {
175-
headers: {'content-length': file.length.toString()},
176-
};
177-
178-
const request = got.stream.post(options)
179-
.on('uploadProgress', event => events.push(event));
180-
181-
await getStream(
182-
stream.pipeline(toReadableStream(file), request, () => {}),
183-
);
184-
185-
checkEvents(t, events, file.length);
186-
});
187-
18854
test('upload progress - stream with unknown body size', withServer, async (t, server, got) => {
18955
server.post('/', uploadEndpoint);
19056

@@ -200,24 +66,3 @@ test('upload progress - stream with unknown body size', withServer, async (t, se
20066
t.is(events[0]?.total, undefined);
20167
checkEvents(t, events);
20268
});
203-
204-
test('upload progress - no body', withServer, async (t, server, got) => {
205-
server.post('/', uploadEndpoint);
206-
207-
const events: Progress[] = [];
208-
209-
await got.post('').on('uploadProgress', (event: Progress) => events.push(event));
210-
211-
t.deepEqual(events, [
212-
{
213-
percent: 0,
214-
transferred: 0,
215-
total: undefined,
216-
},
217-
{
218-
percent: 1,
219-
transferred: 0,
220-
total: 0,
221-
},
222-
]);
223-
});

test/stream.ts

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,6 @@ test('returns writeable stream', withServer, async (t, server, got) => {
9595
t.is(await promise, 'wow');
9696
});
9797

98-
test('throws on write if body is specified', withServer, (t, server, got) => {
99-
server.post('/', postHandler);
100-
101-
const streams = [
102-
got.stream.post({body: 'wow'}),
103-
got.stream.post({json: {}}),
104-
got.stream.post({form: {}}),
105-
];
106-
107-
for (const stream of streams) {
108-
t.throws(() => {
109-
stream.end('wow');
110-
}, {
111-
message: 'The payload has been already provided',
112-
});
113-
114-
stream.destroy();
115-
}
116-
});
117-
11898
test('does not throw if using stream and passing a json option', withServer, async (t, server, got) => {
11999
server.post('/', postHandler);
120100

@@ -127,20 +107,6 @@ test('does not throw if using stream and passing a form option', withServer, asy
127107
await t.notThrowsAsync(getStream(got.stream.post({form: {}})));
128108
});
129109

130-
test('throws on write if no payload method is present', withServer, (t, server, got) => {
131-
server.post('/', postHandler);
132-
133-
const stream = got.stream.get('');
134-
135-
t.throws(() => {
136-
stream.end('wow');
137-
}, {
138-
message: 'The payload has been already provided',
139-
});
140-
141-
stream.destroy();
142-
});
143-
144110
test('has request event', withServer, async (t, server, got) => {
145111
server.get('/', defaultHandler);
146112

0 commit comments

Comments
 (0)