Skip to content

Commit cec8bd5

Browse files
committed
fix: handle request body as late as possible
1 parent 0a069ab commit cec8bd5

4 files changed

Lines changed: 42 additions & 23 deletions

File tree

lib/client.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ const {
105105
// Experimental
106106
let h2ExperimentalWarned = false
107107

108+
let extractBody
109+
108110
const FastBuffer = Buffer[Symbol.species]
109111

110112
const kClosedResolve = Symbol('kClosedResolve')
@@ -1477,7 +1479,9 @@ function write (client, request) {
14771479
return
14781480
}
14791481

1480-
const { body, method, path, host, upgrade, headers, blocking, reset } = request
1482+
const { method, path, host, upgrade, blocking, reset } = request
1483+
1484+
let { body, headers, contentLength } = request
14811485

14821486
// https://tools.ietf.org/html/rfc7231#section-4.3.1
14831487
// https://tools.ietf.org/html/rfc7231#section-4.3.2
@@ -1494,14 +1498,42 @@ function write (client, request) {
14941498
method === 'PATCH'
14951499
)
14961500

1501+
if (util.isFormDataLike(body)) {
1502+
if (!extractBody) {
1503+
extractBody = require('./fetch/body.js').extractBody
1504+
}
1505+
1506+
const [bodyStream, contentType] = extractBody(body)
1507+
if (request.contentType == null) {
1508+
headers += `content-type: ${contentType}\r\n`
1509+
}
1510+
body = bodyStream.stream
1511+
contentLength = bodyStream.length
1512+
} else if (util.isBlobLike(body) && request.contentType == null && body.type) {
1513+
headers += `content-type: ${body.type}\r\n`
1514+
}
1515+
1516+
// TODO (fix): Error if stream is disturbed?
1517+
1518+
if (util.isStream(body) && (body.readableEnded || body.errored)) {
1519+
let err = body.errored
1520+
if (!err) {
1521+
err = new RequestAbortedError()
1522+
util.destroy(body, err)
1523+
}
1524+
body.on('error', () => {})
1525+
errorRequest(client, request, err)
1526+
return false
1527+
}
1528+
14971529
if (body && typeof body.read === 'function') {
14981530
// Try to read EOF in order to get length.
14991531
body.read(0)
15001532
}
15011533

15021534
const bodyLength = util.bodyLength(body)
15031535

1504-
let contentLength = bodyLength
1536+
contentLength = bodyLength ?? contentLength
15051537

15061538
if (contentLength === null) {
15071539
contentLength = request.contentLength
@@ -1544,6 +1576,7 @@ function write (client, request) {
15441576
}
15451577

15461578
if (request.aborted) {
1579+
util.destroy(body)
15471580
return false
15481581
}
15491582

@@ -2050,6 +2083,10 @@ function writeStream ({ h2stream, body, client, request, socket, contentLength,
20502083
socket
20512084
.on('drain', onDrain)
20522085
.on('error', onFinished)
2086+
2087+
if (body.closeEmitted ?? body.closed) {
2088+
queueMicrotask(onClose)
2089+
}
20532090
}
20542091

20552092
async function writeBlob ({ h2stream, body, client, request, socket, contentLength, header, expectsPayload }) {

lib/core/request.js

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ const invalidPathRegex = /[^\u0021-\u00ff]/
2626

2727
const kHandler = Symbol('handler')
2828

29-
let extractBody
30-
3129
class Request {
3230
constructor (origin, {
3331
path,
@@ -173,23 +171,6 @@ class Request {
173171
throw new InvalidArgumentError('headers must be an object or an array')
174172
}
175173

176-
if (util.isFormDataLike(this.body)) {
177-
if (!extractBody) {
178-
extractBody = require('../fetch/body.js').extractBody
179-
}
180-
181-
const [bodyStream, contentType] = extractBody(body)
182-
if (this.contentType == null) {
183-
this.contentType = contentType
184-
this.headers += `content-type: ${contentType}\r\n`
185-
}
186-
this.body = bodyStream.stream
187-
this.contentLength = bodyStream.length
188-
} else if (util.isBlobLike(body) && this.contentType == null && body.type) {
189-
this.contentType = body.type
190-
this.headers += `content-type: ${body.type}\r\n`
191-
}
192-
193174
util.validateHandler(handler, method, upgrade)
194175

195176
this.servername = util.getServerName(this.host)

lib/core/util.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ function bodyLength (body) {
182182
return null
183183
}
184184

185-
function isDestroyed (stream) {
186-
return !stream || !!(stream.destroyed || stream[kDestroyed])
185+
function isDestroyed (body) {
186+
return body && !!(body.destroyed || body[kDestroyed] || (stream.isDestroyed?.(body)))
187187
}
188188

189189
function isReadableAborted (stream) {

test/client.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,7 @@ test('basic POST with empty stream', (t) => {
10551055
method: 'POST',
10561056
body
10571057
}, (err, { statusCode, headers, body }) => {
1058+
console.error(err)
10581059
t.error(err)
10591060
body
10601061
.on('data', () => {

0 commit comments

Comments
 (0)