Skip to content

Commit fee245c

Browse files
fix: HTTP2 tweaks (#2711)
Co-authored-by: Khafra <[email protected]>
1 parent 7308f53 commit fee245c

File tree

2 files changed

+52
-3
lines changed

2 files changed

+52
-3
lines changed

lib/client.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ class Parser {
808808
.removeListener('close', onSocketClose)
809809

810810
client[kSocket] = null
811+
client[kHTTP2Session] = null
811812
client[kQueue][client[kRunningIdx]++] = null
812813
client.emit('disconnect', client[kUrl], [client], new InformationalError('upgrade'))
813814

@@ -1421,7 +1422,7 @@ function _resume (client, sync) {
14211422
return
14221423
}
14231424

1424-
if (!socket && !client[kHTTP2Session]) {
1425+
if (!socket) {
14251426
connect(client)
14261427
return
14271428
}
@@ -1796,7 +1797,25 @@ function writeH2 (client, session, request) {
17961797
})
17971798

17981799
stream.once('end', () => {
1799-
request.onComplete([])
1800+
// When state is null, it means we haven't consumed body and the stream still do not have
1801+
// a state.
1802+
// Present specially when using pipeline or stream
1803+
if (stream.state?.state == null || stream.state.state < 6) {
1804+
request.onComplete([])
1805+
return
1806+
}
1807+
1808+
// Stream is closed or half-closed-remote (6), decrement counter and cleanup
1809+
// It does not have sense to continue working with the stream as we do not
1810+
// have yet RST_STREAM support on client-side
1811+
h2State.openStreams -= 1
1812+
if (h2State.openStreams === 0) {
1813+
session.unref()
1814+
}
1815+
1816+
const err = new InformationalError('HTTP/2: stream half-closed (remote)')
1817+
errorRequest(client, request, err)
1818+
util.destroy(stream, err)
18001819
})
18011820

18021821
stream.on('data', (chunk) => {

test/http2.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const { Client, Agent } = require('..')
1313

1414
const isGreaterThanv20 = process.versions.node.split('.').map(Number)[0] >= 20
1515

16-
plan(24)
16+
plan(25)
1717

1818
test('Should support H2 connection', async t => {
1919
const body = []
@@ -1243,3 +1243,33 @@ test('The h2 pseudo-headers is not included in the headers', async t => {
12431243
t.equal(response.statusCode, 200)
12441244
t.equal(response.headers[':status'], undefined)
12451245
})
1246+
1247+
test('Should throw informational error on half-closed streams (remote)', async t => {
1248+
const server = createSecureServer(pem)
1249+
1250+
server.on('stream', (stream, headers) => {
1251+
stream.destroy()
1252+
})
1253+
1254+
server.listen(0)
1255+
await once(server, 'listening')
1256+
1257+
const client = new Client(`https://localhost:${server.address().port}`, {
1258+
connect: {
1259+
rejectUnauthorized: false
1260+
},
1261+
allowH2: true
1262+
})
1263+
1264+
t.plan(2)
1265+
t.teardown(server.close.bind(server))
1266+
t.teardown(client.close.bind(client))
1267+
1268+
await client.request({
1269+
path: '/',
1270+
method: 'GET'
1271+
}).catch(err => {
1272+
t.equal(err.message, 'HTTP/2: stream half-closed (remote)')
1273+
t.equal(err.code, 'UND_ERR_INFO')
1274+
})
1275+
})

0 commit comments

Comments
 (0)