Skip to content

Commit

Permalink
fix: body error on premature close (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
ronag authored Aug 17, 2020
1 parent 6b01995 commit fc1bee1
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 20 deletions.
15 changes: 9 additions & 6 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,10 @@ class Parser extends HTTPParser {
const { client, socket } = this
const request = client[kQueue][client[kRunningIdx]]

if (socket.destroyed) {
return
}

// TODO: Check for content-length mismatch from server?

assert(!this.upgrade)
Expand Down Expand Up @@ -551,7 +555,7 @@ class Parser extends HTTPParser {
[HTTPParser.kOnBody] (chunk, offset, length) {
const { client, socket, statusCode } = this

if (!statusCode) {
if (socket.destroyed) {
return
}

Expand All @@ -570,7 +574,6 @@ class Parser extends HTTPParser {
// until requests has either completed or socket is destroyed.
if (this.read > client[kMaxAbortedPayload]) {
client[kQueue][client[kRunningIdx]++] = null
this.statusCode = null
util.destroy(socket, new InformationalError('max aborted payload'))
}
} else if (ret === false) {
Expand All @@ -582,7 +585,7 @@ class Parser extends HTTPParser {
const { client, socket, statusCode, headers, upgrade } = this
const request = client[kQueue][client[kRunningIdx]]

if (!statusCode) {
if (socket.destroyed) {
return
}

Expand Down Expand Up @@ -611,17 +614,17 @@ class Parser extends HTTPParser {
// TOOD: keep writing until maxAbortedPayload?

// Response completed before request.
util.destroy(socket, new InformationalError('request reset'))
util.destroy(socket, new InformationalError('reset'))
} else if (client[kReset]) {
// Destroy socket once all requests have completed.
// The request at the tail of the pipeline is the one
// that requested reset and no further requests should
// have been queued since then.
if (!client.running) {
util.destroy(socket, new InformationalError('request reset'))
util.destroy(socket, new InformationalError('reset'))
}
} else if (!this.shouldKeepAlive) {
util.destroy(socket, new InformationalError('request reset'))
util.destroy(socket, new InformationalError('reset'))
} else {
socket.resume()
resume(client)
Expand Down
17 changes: 12 additions & 5 deletions test/client-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ test('socket fail while ending request body', (t) => {
})

test('queued request should not fail on socket destroy', (t) => {
t.plan(2)
t.plan(4)

const server = createServer()
server.on('request', (req, res) => {
Expand All @@ -706,21 +706,25 @@ test('queued request should not fail on socket destroy', (t) => {
method: 'GET'
}, (err, data) => {
t.error(err)
data.body.resume()
data.body.resume().on('error', () => {
t.pass()
})
client[kSocket].destroy()
client.request({
path: '/',
method: 'GET'
}, (err, data) => {
t.error(err)
data.body.resume()
data.body.resume().on('end', () => {
t.pass()
})
})
})
})
})

test('queued request should fail on client destroy', (t) => {
t.plan(5)
t.plan(6)

const server = createServer()
server.on('request', (req, res) => {
Expand All @@ -741,6 +745,9 @@ test('queued request should fail on client destroy', (t) => {
}, (err, data) => {
t.error(err)
data.body.resume()
.on('error', () => {
t.pass()
})
client.destroy((err) => {
t.error(err)
t.strictEqual(requestErrored, true)
Expand Down Expand Up @@ -771,7 +778,7 @@ test('retry idempotent inflight', (t) => {
const client = new Client(`http://localhost:${server.address().port}`, {
pipelining: 3
})
t.tearDown(client.destroy.bind(client))
t.tearDown(client.close.bind(client))

client.request({
path: '/',
Expand Down
8 changes: 6 additions & 2 deletions test/client-reconnect.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { createServer } = require('http')
const FakeTimers = require('@sinonjs/fake-timers')

test('multiple reconnect', (t) => {
t.plan(2)
t.plan(3)

const clock = FakeTimers.install()
t.teardown(clock.uninstall.bind(clock))
Expand All @@ -21,7 +21,11 @@ test('multiple reconnect', (t) => {

client.request({ path: '/', method: 'GET' }, (err, data) => {
t.error(err)
data.body.resume()
data.body
.resume()
.on('end', () => {
t.pass()
})
})

let n = 0
Expand Down
20 changes: 15 additions & 5 deletions test/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,7 @@ test('url-like url', (t) => {
})

test('multiple destroy callback', (t) => {
t.plan(3)
t.plan(4)

const server = createServer((req, res) => {
res.end()
Expand All @@ -637,7 +637,11 @@ test('multiple destroy callback', (t) => {

client.request({ path: '/', method: 'GET' }, (err, data) => {
t.error(err)
data.body.resume()
data.body
.resume()
.on('error', () => {
t.pass()
})
client.destroy(new Error(), (err) => {
t.error(err)
})
Expand All @@ -649,7 +653,7 @@ test('multiple destroy callback', (t) => {
})

test('only one streaming req at a time', (t) => {
t.plan(6)
t.plan(7)

const server = createServer((req, res) => {
req.pipe(res)
Expand Down Expand Up @@ -693,15 +697,19 @@ test('only one streaming req at a time', (t) => {
})
}, (err, data) => {
t.error(err)
data.body.resume()
data.body
.resume()
.on('end', () => {
t.pass()
})
})
t.strictEqual(client.busy, true)
})
})
})

test('300 requests succeed', (t) => {
t.plan(300 * 2)
t.plan(300 * 3)

const server = createServer((req, res) => {
res.end('asd')
Expand All @@ -720,6 +728,8 @@ test('300 requests succeed', (t) => {
t.error(err)
data.body.on('data', (chunk) => {
t.strictEqual(chunk.toString(), 'asd')
}).on('end', () => {
t.pass()
})
})
}
Expand Down
4 changes: 3 additions & 1 deletion test/pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ test('pool pipeline args validation', (t) => {
})

test('300 requests succeed', (t) => {
t.plan(300 * 2)
t.plan(300 * 3)

const server = createServer((req, res) => {
res.end('asd')
Expand All @@ -461,6 +461,8 @@ test('300 requests succeed', (t) => {
t.error(err)
data.body.on('data', (chunk) => {
t.strictEqual(chunk.toString(), 'asd')
}).on('end', () => {
t.pass()
})
})
}
Expand Down
7 changes: 6 additions & 1 deletion test/request-timeout.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ test('request timeout immutable opts', (t) => {
})

test('Subsequent request starves', (t) => {
t.plan(2)
t.plan(3)

const clock = FakeTimers.install()
t.teardown(clock.uninstall.bind(clock))
Expand All @@ -89,6 +89,11 @@ test('Subsequent request starves', (t) => {

client.request({ path: '/', method: 'GET' }, (err, response) => {
t.error(err)
response.body
.resume()
.on('end', () => {
t.pass()
})
})

client.request({ path: '/', method: 'GET', requestTimeout: 50 }, (err, response) => {
Expand Down

0 comments on commit fc1bee1

Please sign in to comment.