Skip to content

Commit 8d744a2

Browse files
committed
fixup: tests
1 parent ba51fe5 commit 8d744a2

File tree

5 files changed

+454
-62
lines changed

5 files changed

+454
-62
lines changed

docs/api/Dispatcher.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ The `RequestOptions.method` property should not be value `'CONNECT'`.
418418

419419
* **statusCode** `number`
420420
* **headers** `http.IncomingHttpHeaders`
421-
* **body** `stream.Readable`
421+
* **body** `stream.Readable` which also implements [the body mixin from the Fetch Standard](https://fetch.spec.whatwg.org/#body-mixin).
422422
* **trailers** `Record<string, string>` - This object starts out
423423
as empty and will be mutated to contain trailers after `body` has emitted `'end'`.
424424
* **opaque** `unknown`

lib/api/api-request.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const Readable = require('./readable')
3+
const Readable = require('../node/readable')
44
const {
55
InvalidArgumentError,
66
RequestAbortedError

lib/api/readable.js renamed to lib/node/readable.js

Lines changed: 64 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
1+
/* istanbul ignore file: TODO add more coverage */
2+
3+
// Ported from https://github.com/nodejs/undici/pull/907
4+
15
'use strict'
26

7+
const assert = require('assert')
38
const { Readable } = require('stream')
4-
const { InvalidArgumentError } = require('../core/errors')
9+
const { InvalidArgumentError, RequestAbortedError } = require('../core/errors')
510

611
let StringDecoder
712
let Blob
813

914
const kConsume = Symbol('kConsume')
1015
const kReading = Symbol('kReading')
16+
const kBody = Symbol('kBody')
1117

12-
const kWebStreamType = 1
13-
const kTextType = 2
14-
const kBlobType = 3
15-
const kArrayBufferType = 4
16-
const kJSONType = 5
17-
18-
class AbortError extends Error {
19-
constructor (message) {
20-
super(message)
21-
Error.captureStackTrace(this, AbortError)
22-
this.name = 'AbortError'
23-
this.message = 'aborted'
24-
this.code = 'UND_ERR_ABORTED'
25-
}
26-
}
18+
const kTextType = 1
19+
const kBlobType = 2
20+
const kArrayBufferType = 3
21+
const kJSONType = 4
2722

2823
module.exports = class BodyReadable extends Readable {
2924
constructor (opts) {
@@ -32,12 +27,24 @@ module.exports = class BodyReadable extends Readable {
3227
this._readableState.dataEmitted = false
3328

3429
this[kConsume] = null
30+
this[kBody] = null
3531
this[kReading] = false // Is stream being consumed through Readable API?
3632
}
3733

34+
destroy (err) {
35+
if (this.destroyed) {
36+
// Node < 16
37+
return this
38+
}
39+
return super.destroy(err)
40+
}
41+
3842
emit (ev, ...args) {
3943
if (ev === 'data') {
4044
this._readableState.dataEmitted = true
45+
} else if (ev === 'error') {
46+
// Node < 16
47+
this._readableState.errorEmitted = true
4148
}
4249
return super.emit(ev, ...args)
4350
}
@@ -49,6 +56,13 @@ module.exports = class BodyReadable extends Readable {
4956
return super.on(ev, ...args)
5057
}
5158

59+
addListener (ev, ...args) {
60+
if (ev === 'data' || ev === 'readable') {
61+
this[kReading] = true
62+
}
63+
return super.addListener(ev, ...args)
64+
}
65+
5266
push (chunk, encoding) {
5367
if (this[kConsume] && chunk !== null && !this[kReading]) {
5468
// Fast path.
@@ -90,12 +104,16 @@ module.exports = class BodyReadable extends Readable {
90104
return isDisturbed(this)
91105
}
92106

107+
// https://fetch.spec.whatwg.org/#dom-body-body
93108
get body () {
94-
if (this[kConsume] && this[kConsume].type === kWebStreamType) {
95-
return this[kConsume].stream
109+
if (!this[kBody]) {
110+
if (isUnusable(this)) {
111+
throw new TypeError('unusable')
112+
}
113+
this[kBody] = Readable.toWeb(this)
96114
}
97115

98-
return consume(this, kWebStreamType)
116+
return this[kBody]
99117
}
100118
}
101119

@@ -123,32 +141,17 @@ function isUnusable (self) {
123141
return isDisturbed(self) || isLocked(self)
124142
}
125143

126-
async function consume (parent, type) {
127-
if (isUnusable(parent)) {
128-
// eslint-disable-next-line no-restricted-syntax
144+
async function consume (stream, type) {
145+
if (isUnusable(stream)) {
129146
throw new TypeError('unusable')
130147
}
131148

132-
if (parent[kConsume]) {
133-
// TODO: Should multiple consume in same tick be possible?
134-
// eslint-disable-next-line no-restricted-syntax
135-
throw new TypeError('unusable')
136-
}
137-
138-
if (type === kWebStreamType) {
139-
const consume = parent[kConsume] = {
140-
type,
141-
// TODO: Optimized implementation for web streams.
142-
stream: Readable.toWeb(parent)
143-
}
144-
145-
return consume.stream
146-
}
149+
assert(!stream[kConsume])
147150

148151
return new Promise((resolve, reject) => {
149-
parent[kConsume] = {
152+
stream[kConsume] = {
150153
type,
151-
parent,
154+
stream,
152155
resolve,
153156
reject,
154157
length: 0,
@@ -159,17 +162,18 @@ async function consume (parent, type) {
159162
ended: false
160163
}
161164

162-
parent
165+
stream
163166
.once('error', function (err) {
164167
consumeFinish(this[kConsume], err)
165168
})
166169
.once('close', function () {
167170
if (this[kConsume].body !== null) {
168-
consumeFinish(this[kConsume], new AbortError())
171+
// TODO: Use Node error?
172+
consumeFinish(this[kConsume], new RequestAbortedError())
169173
}
170174
})
171175

172-
process.nextTick(consumeStart, parent[kConsume])
176+
process.nextTick(consumeStart, stream[kConsume])
173177
})
174178
}
175179

@@ -178,7 +182,7 @@ function consumeStart (consume) {
178182
return
179183
}
180184

181-
const { _readableState: state } = consume.parent
185+
const { _readableState: state } = consume.stream
182186

183187
for (const chunk of state.buffer) {
184188
consumePush(consume, chunk)
@@ -187,20 +191,18 @@ function consumeStart (consume) {
187191
if (state.endEmitted) {
188192
consumeEnd(this[kConsume])
189193
} else {
190-
consume.parent.once('end', function () {
194+
consume.stream.once('end', function () {
191195
consumeEnd(this[kConsume])
192196
})
193197
}
194198

195-
if (consume.parent.isPaused()) {
196-
consume.parent.resume()
197-
}
199+
consume.stream.resume()
198200

199-
while (consume.parent.read() != null);
201+
while (consume.stream.read() != null);
200202
}
201203

202204
function consumeEnd (consume) {
203-
const { type, body, resolve, decoder, parent, length } = consume
205+
const { type, body, resolve, decoder, stream, length } = consume
204206

205207
try {
206208
if (type === kTextType) {
@@ -226,7 +228,7 @@ function consumeEnd (consume) {
226228

227229
consumeFinish(consume)
228230
} catch (err) {
229-
parent.destroy(err)
231+
stream.destroy(err)
230232
}
231233
}
232234

@@ -241,7 +243,7 @@ function consumePush (consume, chunk, encoding) {
241243

242244
if (chunk === null) {
243245
consume.ended = true
244-
consume.parent.read()
246+
consume.stream.read()
245247
return false
246248
}
247249

@@ -253,7 +255,7 @@ function consumePush (consume, chunk, encoding) {
253255
consumePushBuffer(consume, chunk, encoding)
254256
}
255257

256-
if (!consume.parent[kReading] && !consume.reading) {
258+
if (!consume.stream[kReading] && !consume.reading) {
257259
consume.reading = true
258260
process.nextTick(consumeReadMore, consume)
259261
}
@@ -283,6 +285,7 @@ function consumePushString (consume, chunk, encoding) {
283285
} else {
284286
// TODO: What if objectMode? Should we just fail consume
285287
// or throw?
288+
// TODO: Use Node error?
286289
throw new InvalidArgumentError('chunk')
287290
}
288291

@@ -300,6 +303,7 @@ function consumePushBuffer (consume, chunk, encoding) {
300303
} else if (!ArrayBuffer.isView(chunk)) {
301304
// TODO: What if objectMode? Should we just fail consume
302305
// or throw?
306+
// TODO: Use Node error?
303307
throw new InvalidArgumentError('chunk')
304308
}
305309

@@ -308,21 +312,22 @@ function consumePushBuffer (consume, chunk, encoding) {
308312
}
309313

310314
function consumeReadMore (consume) {
311-
if (consume.parent[kReading]) {
312-
consume.reading = false
313-
return
314-
}
315-
316315
consume.pushed = true
317-
while (consume.pushed) {
316+
317+
while (consume.pushed && !consume.stream[kReading]) {
318318
consume.pushed = false
319-
consume.parent._read(consume.parent)
319+
consume.stream._read(consume.stream)
320320
}
321321

322+
consume.pushed = false
322323
consume.reading = false
323324
}
324325

325326
function consumeFinish (consume, err) {
327+
if (consume.body === null) {
328+
return
329+
}
330+
326331
if (err) {
327332
consume.reject(err)
328333
} else {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "undici",
3-
"version": "4.3.0",
3+
"version": "4.2.2",
44
"description": "An HTTP/1.1 client, written from scratch for Node.js",
55
"homepage": "https://undici.nodejs.org",
66
"bugs": {

0 commit comments

Comments
 (0)